CINXE.COM
GitHub - tomitribe/crest: Command-line API modeled after JAX-RS (Command REST)
<!DOCTYPE html> <html lang="en" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark" data-a11y-animated-images="system" data-a11y-link-underlines="true" > <head> <meta charset="utf-8"> <link rel="dns-prefetch" href="https://github.githubassets.com"> <link rel="dns-prefetch" href="https://avatars.githubusercontent.com"> <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com"> <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/"> <link rel="preconnect" href="https://github.githubassets.com" crossorigin> <link rel="preconnect" href="https://avatars.githubusercontent.com"> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/light-74231a1f3bbb.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/dark-8a995f0bacd4.css" /><link data-color-theme="dark_dimmed" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_dimmed-f37fb7684b1f.css" /><link data-color-theme="dark_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_high_contrast-9ac301c3ebe5.css" /><link data-color-theme="dark_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_colorblind-cd826e8636dc.css" /><link data-color-theme="light_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_colorblind-f91b0f603451.css" /><link data-color-theme="light_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_high_contrast-83beb16e0ecf.css" /><link data-color-theme="light_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_tritanopia-6e122dab64fc.css" /><link data-color-theme="dark_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_tritanopia-18119e682df0.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-primitives-225433424a87.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-cba26849680f.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-b6cb3703b934.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/github-ea73c9cb5377.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/repository-4fce88777fa8.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/code-0210be90f4d3.css" /> <script type="application/json" id="client-env">{"locale":"en","featureFlags":["contentful_lp_flex_features_actions","contentful_lp_flex_features_codespaces","contentful_lp_flex_features_code_review","contentful_lp_flex_features_code_search","contentful_lp_flex_features_discussions","contentful_lp_flex_features_issues","copilot_immersive_issue_preview","copilot_new_references_ui","copilot_chat_repo_custom_instructions_preview","copilot_chat_show_model_picker_on_retry","copilot_no_floating_button","copilot_topics_as_references","copilot_read_shared_conversation","copilot_duplicate_thread","dotcom_chat_client_side_skills","experimentation_azure_variant_endpoint","failbot_handle_non_errors","geojson_azure_maps","ghost_pilot_confidence_truncation_25","ghost_pilot_confidence_truncation_40","github_models_gateway_parse_params","github_models_o3_mini_streaming","insert_before_patch","issues_advanced_search_has_filter","issues_react_remove_placeholders","issues_react_blur_item_picker_on_close","issues_advanced_search_nested_ownership_filters","issues_dashboard_no_redirects","marketing_pages_search_explore_provider","primer_react_css_modules_ga","react_data_router_pull_requests","remove_child_patch","sample_network_conn_type","swp_enterprise_contact_form","site_copilot_pro_plus","site_proxima_australia_update","viewscreen_sandbox","issues_react_create_milestone","issues_react_cache_fix_workaround","lifecycle_label_name_updates","copilot_task_oriented_assistive_prompts","issue_types_prevent_private_type_creation","refresh_image_video_src","codespaces_prebuild_region_target_update","copilot_code_review_sign_up_closed"]}</script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/wp-runtime-5bf663867709.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_oddbird_popover-polyfill_dist_popover_js-9da652f58479.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_arianotify-polyfill_ariaNotify-polyfill_js-node_modules_github_mi-3abb8f-46b9f4874d95.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_failbot_failbot_ts-952d624642a1.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/environment-f04cb2a9fc8c.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_primer_behaviors_dist_esm_index_mjs-0dbb79f97f8f.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_selector-observer_dist_index_esm_js-f690fd9ae3d5.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_relative-time-element_dist_index_js-62d275b7ddd9.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_text-expander-element_dist_index_js-78748950cb0c.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_auto-complete-element_dist_index_js-node_modules_github_catalyst_-8e9f78-a90ac05d2469.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_filter-input-element_dist_index_js-node_modules_github_remote-inp-b5f1d7-a1760ffda83d.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_markdown-toolbar-element_dist_index_js-ceef33f593fa.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_file-attachment-element_dist_index_js-node_modules_primer_view-co-c44a69-efa32db3a345.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/github-elements-394f8eb34f19.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/element-registry-0b7798be0424.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_braintree_browser-detection_dist_browser-detection_js-node_modules_githu-2906d7-2a07a295af40.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_lit-html_lit-html_js-be8cb88f481b.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_index_js-node_modules_morphdom_dist_morphdom-e-7c534c-a4a1922eb55f.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_turbo_dist_turbo_es2017-esm_js-a03ee12d659a.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_remote-form_dist_index_js-node_modules_delegated-events_dist_inde-893f9f-b6294cf703b7.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_color-convert_index_js-e3180fe3bcb3.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_quote-selection_dist_index_js-node_modules_github_session-resume_-947061-e7a6c4a19f98.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_updatable-content_updatable-content_ts-62f3e9c52ece.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_behaviors_task-list_ts-app_assets_modules_github_sso_ts-ui_packages-900dde-768abe60b1f8.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_sticky-scroll-into-view_ts-3e000c5d31a9.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_behaviors_ajax-error_ts-app_assets_modules_github_behaviors_include-d0d0a6-e7f74ee74d91.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_behaviors_commenting_edit_ts-app_assets_modules_github_behaviors_ht-83c235-4bcbbbfbe1d4.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/behaviors-4414ad8b510b.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_delegated-events_dist_index_js-node_modules_github_catalyst_lib_index_js-f6223d90c7ba.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/notifications-global-01e85cd1be94.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_virtualized-list_es_index_js-node_modules_github_template-parts_lib_index_js-94dc7a2157c1.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_remote-form_dist_index_js-node_modules_delegated-events_dist_inde-70450e-4b93df70b903.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_ref-selector_ts-52913063a0b9.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/codespaces-b419a25ee02f.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_filter-input-element_dist_index_js-node_modules_github_remote-inp-3eebbd-0763620ad7bf.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_decorators_js-node_modules_delegated-events_di-e161aa-9d41fb1b6c9e.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_file-attachment-element_dist_index_js-node_modules_github_remote--3c9c82-b71ef90fbdc7.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/repositories-10217e4e5a53.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_index_js-node_modules_github_catalyst_lib_inde-dbbea9-26cce2010167.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/code-menu-d6d3c94ee97e.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/primer-react-350730ea92ff.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/react-core-72be69cef469.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/react-lib-1622bd1e542f.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/octicons-react-cf2f2ab8dab4.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_emotion_is-prop-valid_dist_emotion-is-prop-valid_esm_js-node_modules_emo-41b1a8-555bc0cf9cab.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_index_js-node_modules_stacktrace-parser_dist_s-e7dcdd-9a233856b02c.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_oddbird_popover-polyfill_dist_popover-fn_js-55fea94174bf.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/notifications-subscriptions-menu-53aa08c61b34.js"></script> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react.a490b7c9fa319e5cb069.module.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/notifications-subscriptions-menu.1bcff9205c241e99cff2.module.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react.a490b7c9fa319e5cb069.module.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/notifications-subscriptions-menu.1bcff9205c241e99cff2.module.css" /> <title>GitHub - tomitribe/crest: Command-line API modeled after JAX-RS (Command REST)</title> <meta name="route-pattern" content="/:user_id/:repository" data-turbo-transient> <meta name="route-controller" content="files" data-turbo-transient> <meta name="route-action" content="disambiguate" data-turbo-transient> <meta name="current-catalog-service-hash" content="f3abb0cc802f3d7b95fc8762b94bdcb13bf39634c40c357301c4aa1d67a256fb"> <meta name="request-id" content="A55E:DE242:91824C:A3BCC8:67F631F6" data-pjax-transient="true"/><meta name="html-safe-nonce" content="64836eb7c61217596f82590ccec5b895b1b7afee2b7ff5e5efb54ab5fb179638" data-pjax-transient="true"/><meta name="visitor-payload" content="eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBNTVFOkRFMjQyOjkxODI0QzpBM0JDQzg6NjdGNjMxRjYiLCJ2aXNpdG9yX2lkIjoiODkyNTgzODI3Nzk1MjgxMTUxMCIsInJlZ2lvbl9lZGdlIjoic291dGhlYXN0YXNpYSIsInJlZ2lvbl9yZW5kZXIiOiJzb3V0aGVhc3Rhc2lhIn0=" data-pjax-transient="true"/><meta name="visitor-hmac" content="d3c73eb8b75328d77f31dcf9166fa2b73a1cdc230646b3a8bd7e2072c215c2d7" data-pjax-transient="true"/> <meta name="hovercard-subject-tag" content="repository:11673895" data-turbo-transient> <meta name="github-keyboard-shortcuts" content="repository,copilot" data-turbo-transient="true" /> <meta name="selected-link" value="repo_source" data-turbo-transient> <link rel="assets" href="https://github.githubassets.com/"> <meta name="google-site-verification" content="Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I"> <meta name="octolytics-url" content="https://collector.github.com/github/collect" /> <meta name="analytics-location" content="/<user-name>/<repo-name>" data-turbo-transient="true" /> <meta name="user-login" content=""> <meta name="viewport" content="width=device-width"> <meta name="description" content="Command-line API modeled after JAX-RS (Command REST) - tomitribe/crest"> <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub"> <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub"> <meta property="fb:app_id" content="1401488693436528"> <meta name="apple-itunes-app" content="app-id=1477376905, app-argument=https://github.com/tomitribe/crest" /> <meta name="twitter:image" content="https://opengraph.githubassets.com/ebed2831f6e4bd84c5451a3088e521f887d968b9ee589c97ae489df172b8db4c/tomitribe/crest" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:title" content="GitHub - tomitribe/crest: Command-line API modeled after JAX-RS (Command REST)" /><meta name="twitter:description" content="Command-line API modeled after JAX-RS (Command REST) - tomitribe/crest" /> <meta property="og:image" content="https://opengraph.githubassets.com/ebed2831f6e4bd84c5451a3088e521f887d968b9ee589c97ae489df172b8db4c/tomitribe/crest" /><meta property="og:image:alt" content="Command-line API modeled after JAX-RS (Command REST) - tomitribe/crest" /><meta property="og:image:width" content="1200" /><meta property="og:image:height" content="600" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="GitHub - tomitribe/crest: Command-line API modeled after JAX-RS (Command REST)" /><meta property="og:url" content="https://github.com/tomitribe/crest" /><meta property="og:description" content="Command-line API modeled after JAX-RS (Command REST) - tomitribe/crest" /> <meta name="hostname" content="github.com"> <meta name="expected-hostname" content="github.com"> <meta http-equiv="x-pjax-version" content="fe109c1aad228d6e2c761f16c20e91bb8457e71a568383631a12df5280057763" data-turbo-track="reload"> <meta http-equiv="x-pjax-csp-version" content="e26f9f0ba624ee85cc7ac057d8faa8618a4f25a85eab052c33d018ac0f6b1a46" data-turbo-track="reload"> <meta http-equiv="x-pjax-css-version" content="205838381d6e5f35c535dbb12458f905bc43e0b186c86bf75aabbd0c0f36537c" data-turbo-track="reload"> <meta http-equiv="x-pjax-js-version" content="7bc19fb7b7e61562272d4e42faad9e2a473aa738eaae3ba22d3feee94c6a7c75" data-turbo-track="reload"> <meta name="turbo-cache-control" content="no-preview" data-turbo-transient=""> <meta data-hydrostats="publish"> <meta name="go-import" content="github.com/tomitribe/crest git https://github.com/tomitribe/crest.git"> <meta name="octolytics-dimension-user_id" content="2865265" /><meta name="octolytics-dimension-user_login" content="tomitribe" /><meta name="octolytics-dimension-repository_id" content="11673895" /><meta name="octolytics-dimension-repository_nwo" content="tomitribe/crest" /><meta name="octolytics-dimension-repository_public" content="true" /><meta name="octolytics-dimension-repository_is_fork" content="false" /><meta name="octolytics-dimension-repository_network_root_id" content="11673895" /><meta name="octolytics-dimension-repository_network_root_nwo" content="tomitribe/crest" /> <link rel="canonical" href="https://github.com/tomitribe/crest" data-turbo-transient> <meta name="turbo-body-classes" content="logged-out env-production page-responsive"> <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats"> <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors"> <meta name="release" content="75aadf2542d801112adc89fbd700a13214b27c23"> <link rel="mask-icon" href="https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg" color="#000000"> <link rel="alternate icon" class="js-site-favicon" type="image/png" href="https://github.githubassets.com/favicons/favicon.png"> <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg" data-base-href="https://github.githubassets.com/favicons/favicon"> <meta name="theme-color" content="#1e2327"> <meta name="color-scheme" content="light dark" /> <link rel="manifest" href="/manifest.json" crossOrigin="use-credentials"> </head> <body class="logged-out env-production page-responsive" style="word-wrap: break-word;"> <div data-turbo-body class="logged-out env-production page-responsive" style="word-wrap: break-word;"> <div class="position-relative header-wrapper js-header-wrapper "> <a href="#start-of-content" data-skip-target-assigned="false" class="px-2 py-4 color-bg-accent-emphasis color-fg-on-emphasis show-on-focus js-skip-to-content">Skip to content</a> <span data-view-component="true" class="progress-pjax-loader Progress position-fixed width-full"> <span style="width: 0%;" data-view-component="true" class="Progress-item progress-pjax-loader-bar left-0 top-0 color-bg-accent-emphasis"></span> </span> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_ui-commands_ui-commands_ts-2d52c8e72e64.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/keyboard-shortcuts-dialog-2560f573c7ca.js"></script> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react.a490b7c9fa319e5cb069.module.css" /> <react-partial partial-name="keyboard-shortcuts-dialog" data-ssr="false" data-attempted-ssr="false" > <script type="application/json" data-target="react-partial.embeddedData">{"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}</script> <div data-target="react-partial.reactRoot"></div> </react-partial> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_remote-form_dist_index_js-node_modules_delegated-events_dist_inde-94fd67-4898d1bf4b51.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/sessions-45d6658f8b6b.js"></script> <header class="HeaderMktg header-logged-out js-details-container js-header Details f4 py-3" role="banner" data-is-top="true" data-color-mode=light data-light-theme=light data-dark-theme=dark> <h2 class="sr-only">Navigation Menu</h2> <button type="button" class="HeaderMktg-backdrop d-lg-none border-0 position-fixed top-0 left-0 width-full height-full js-details-target" aria-label="Toggle navigation"> <span class="d-none">Toggle navigation</span> </button> <div class="d-flex flex-column flex-lg-row flex-items-center px-3 px-md-4 px-lg-5 height-full position-relative z-1"> <div class="d-flex flex-justify-between flex-items-center width-full width-lg-auto"> <div class="flex-1"> <button aria-label="Toggle navigation" aria-expanded="false" type="button" data-view-component="true" class="js-details-target js-nav-padding-recalculate js-header-menu-toggle Button--link Button--medium Button d-lg-none color-fg-inherit p-1"> <span class="Button-content"> <span class="Button-label"><div class="HeaderMenu-toggle-bar rounded my-1"></div> <div class="HeaderMenu-toggle-bar rounded my-1"></div> <div class="HeaderMenu-toggle-bar rounded my-1"></div></span> </span> </button> </div> <a class="mr-lg-3 color-fg-inherit flex-order-2 js-prevent-focus-on-mobile-nav" href="/" aria-label="Homepage" data-analytics-event="{"category":"Marketing nav","action":"click to go to homepage","label":"ref_page:Marketing;ref_cta:Logomark;ref_loc:Header"}"> <svg height="32" aria-hidden="true" viewBox="0 0 24 24" version="1.1" width="32" data-view-component="true" class="octicon octicon-mark-github"> <path d="M12 1C5.9225 1 1 5.9225 1 12C1 16.8675 4.14875 20.9787 8.52125 22.4362C9.07125 22.5325 9.2775 22.2025 9.2775 21.9137C9.2775 21.6525 9.26375 20.7862 9.26375 19.865C6.5 20.3737 5.785 19.1912 5.565 18.5725C5.44125 18.2562 4.905 17.28 4.4375 17.0187C4.0525 16.8125 3.5025 16.3037 4.42375 16.29C5.29 16.2762 5.90875 17.0875 6.115 17.4175C7.105 19.0812 8.68625 18.6137 9.31875 18.325C9.415 17.61 9.70375 17.1287 10.02 16.8537C7.5725 16.5787 5.015 15.63 5.015 11.4225C5.015 10.2262 5.44125 9.23625 6.1425 8.46625C6.0325 8.19125 5.6475 7.06375 6.2525 5.55125C6.2525 5.55125 7.17375 5.2625 9.2775 6.67875C10.1575 6.43125 11.0925 6.3075 12.0275 6.3075C12.9625 6.3075 13.8975 6.43125 14.7775 6.67875C16.8813 5.24875 17.8025 5.55125 17.8025 5.55125C18.4075 7.06375 18.0225 8.19125 17.9125 8.46625C18.6138 9.23625 19.04 10.2125 19.04 11.4225C19.04 15.6437 16.4688 16.5787 14.0213 16.8537C14.42 17.1975 14.7638 17.8575 14.7638 18.8887C14.7638 20.36 14.75 21.5425 14.75 21.9137C14.75 22.2025 14.9563 22.5462 15.5063 22.4362C19.8513 20.9787 23 16.8537 23 12C23 5.9225 18.0775 1 12 1Z"></path> </svg> </a> <div class="flex-1 flex-order-2 text-right"> <a href="/login?return_to=https%3A%2F%2Fgithub.com%2Ftomitribe%2Fcrest" class="HeaderMenu-link HeaderMenu-button d-inline-flex d-lg-none flex-order-1 f5 no-underline border color-border-default rounded-2 px-2 py-1 color-fg-inherit js-prevent-focus-on-mobile-nav" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/tomitribe/crest","user_id":null}}" data-hydro-click-hmac="85a7b0fed772de6cb6cc65108d060ef226fafb4358d588211ca909ae4f50ac36" data-analytics-event="{"category":"Marketing nav","action":"click to Sign in","label":"ref_page:Marketing;ref_cta:Sign in;ref_loc:Header"}" > Sign in </a> </div> </div> <div class="HeaderMenu js-header-menu height-fit position-lg-relative d-lg-flex flex-column flex-auto top-0"> <div class="HeaderMenu-wrapper d-flex flex-column flex-self-start flex-lg-row flex-auto rounded rounded-lg-0"> <nav class="HeaderMenu-nav" aria-label="Global"> <ul class="d-lg-flex list-style-none"> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Product <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 pb-2 pb-lg-4 d-lg-flex flex-wrap dropdown-menu-wide"> <div class="HeaderMenu-column px-lg-4 border-lg-right mb-4 mb-lg-0 pr-lg-7"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0"> <ul class="list-style-none f5" > <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"github_copilot","context":"product","tag":"link","label":"github_copilot_link_product_navbar"}" href="https://github.com/features/copilot"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-copilot color-fg-subtle mr-3"> <path d="M23.922 16.992c-.861 1.495-5.859 5.023-11.922 5.023-6.063 0-11.061-3.528-11.922-5.023A.641.641 0 0 1 0 16.736v-2.869a.841.841 0 0 1 .053-.22c.372-.935 1.347-2.292 2.605-2.656.167-.429.414-1.055.644-1.517a10.195 10.195 0 0 1-.052-1.086c0-1.331.282-2.499 1.132-3.368.397-.406.89-.717 1.474-.952 1.399-1.136 3.392-2.093 6.122-2.093 2.731 0 4.767.957 6.166 2.093.584.235 1.077.546 1.474.952.85.869 1.132 2.037 1.132 3.368 0 .368-.014.733-.052 1.086.23.462.477 1.088.644 1.517 1.258.364 2.233 1.721 2.605 2.656a.832.832 0 0 1 .053.22v2.869a.641.641 0 0 1-.078.256ZM12.172 11h-.344a4.323 4.323 0 0 1-.355.508C10.703 12.455 9.555 13 7.965 13c-1.725 0-2.989-.359-3.782-1.259a2.005 2.005 0 0 1-.085-.104L4 11.741v6.585c1.435.779 4.514 2.179 8 2.179 3.486 0 6.565-1.4 8-2.179v-6.585l-.098-.104s-.033.045-.085.104c-.793.9-2.057 1.259-3.782 1.259-1.59 0-2.738-.545-3.508-1.492a4.323 4.323 0 0 1-.355-.508h-.016.016Zm.641-2.935c.136 1.057.403 1.913.878 2.497.442.544 1.134.938 2.344.938 1.573 0 2.292-.337 2.657-.751.384-.435.558-1.15.558-2.361 0-1.14-.243-1.847-.705-2.319-.477-.488-1.319-.862-2.824-1.025-1.487-.161-2.192.138-2.533.529-.269.307-.437.808-.438 1.578v.021c0 .265.021.562.063.893Zm-1.626 0c.042-.331.063-.628.063-.894v-.02c-.001-.77-.169-1.271-.438-1.578-.341-.391-1.046-.69-2.533-.529-1.505.163-2.347.537-2.824 1.025-.462.472-.705 1.179-.705 2.319 0 1.211.175 1.926.558 2.361.365.414 1.084.751 2.657.751 1.21 0 1.902-.394 2.344-.938.475-.584.742-1.44.878-2.497Z"></path><path d="M14.5 14.25a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1Zm-5 0a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1Z"></path> </svg> <div> <div class="color-fg-default h4">GitHub Copilot</div> Write better code with AI </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"github_advanced_security","context":"product","tag":"link","label":"github_advanced_security_link_product_navbar"}" href="https://github.com/security/advanced-security"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-shield-check color-fg-subtle mr-3"> <path d="M16.53 9.78a.75.75 0 0 0-1.06-1.06L11 13.19l-1.97-1.97a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l5-5Z"></path><path d="m12.54.637 8.25 2.675A1.75 1.75 0 0 1 22 4.976V10c0 6.19-3.771 10.704-9.401 12.83a1.704 1.704 0 0 1-1.198 0C5.77 20.705 2 16.19 2 10V4.976c0-.758.489-1.43 1.21-1.664L11.46.637a1.748 1.748 0 0 1 1.08 0Zm-.617 1.426-8.25 2.676a.249.249 0 0 0-.173.237V10c0 5.46 3.28 9.483 8.43 11.426a.199.199 0 0 0 .14 0C17.22 19.483 20.5 15.461 20.5 10V4.976a.25.25 0 0 0-.173-.237l-8.25-2.676a.253.253 0 0 0-.154 0Z"></path> </svg> <div> <div class="color-fg-default h4">GitHub Advanced Security</div> Find and fix vulnerabilities </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"actions","context":"product","tag":"link","label":"actions_link_product_navbar"}" href="https://github.com/features/actions"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-workflow color-fg-subtle mr-3"> <path d="M1 3a2 2 0 0 1 2-2h6.5a2 2 0 0 1 2 2v6.5a2 2 0 0 1-2 2H7v4.063C7 16.355 7.644 17 8.438 17H12.5v-2.5a2 2 0 0 1 2-2H21a2 2 0 0 1 2 2V21a2 2 0 0 1-2 2h-6.5a2 2 0 0 1-2-2v-2.5H8.437A2.939 2.939 0 0 1 5.5 15.562V11.5H3a2 2 0 0 1-2-2Zm2-.5a.5.5 0 0 0-.5.5v6.5a.5.5 0 0 0 .5.5h6.5a.5.5 0 0 0 .5-.5V3a.5.5 0 0 0-.5-.5ZM14.5 14a.5.5 0 0 0-.5.5V21a.5.5 0 0 0 .5.5H21a.5.5 0 0 0 .5-.5v-6.5a.5.5 0 0 0-.5-.5Z"></path> </svg> <div> <div class="color-fg-default h4">Actions</div> Automate any workflow </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"codespaces","context":"product","tag":"link","label":"codespaces_link_product_navbar"}" href="https://github.com/features/codespaces"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-codespaces color-fg-subtle mr-3"> <path d="M3.5 3.75C3.5 2.784 4.284 2 5.25 2h13.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 18.75 13H5.25a1.75 1.75 0 0 1-1.75-1.75Zm-2 12c0-.966.784-1.75 1.75-1.75h17.5c.966 0 1.75.784 1.75 1.75v4a1.75 1.75 0 0 1-1.75 1.75H3.25a1.75 1.75 0 0 1-1.75-1.75ZM5.25 3.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h13.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Zm-2 12a.25.25 0 0 0-.25.25v4c0 .138.112.25.25.25h17.5a.25.25 0 0 0 .25-.25v-4a.25.25 0 0 0-.25-.25Z"></path><path d="M10 17.75a.75.75 0 0 1 .75-.75h6.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1-.75-.75Zm-4 0a.75.75 0 0 1 .75-.75h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1-.75-.75Z"></path> </svg> <div> <div class="color-fg-default h4">Codespaces</div> Instant dev environments </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"issues","context":"product","tag":"link","label":"issues_link_product_navbar"}" href="https://github.com/features/issues"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-issue-opened color-fg-subtle mr-3"> <path d="M12 1c6.075 0 11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1ZM2.5 12a9.5 9.5 0 0 0 9.5 9.5 9.5 9.5 0 0 0 9.5-9.5A9.5 9.5 0 0 0 12 2.5 9.5 9.5 0 0 0 2.5 12Zm9.5 2a2 2 0 1 1-.001-3.999A2 2 0 0 1 12 14Z"></path> </svg> <div> <div class="color-fg-default h4">Issues</div> Plan and track work </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"code_review","context":"product","tag":"link","label":"code_review_link_product_navbar"}" href="https://github.com/features/code-review"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-code-review color-fg-subtle mr-3"> <path d="M10.3 6.74a.75.75 0 0 1-.04 1.06l-2.908 2.7 2.908 2.7a.75.75 0 1 1-1.02 1.1l-3.5-3.25a.75.75 0 0 1 0-1.1l3.5-3.25a.75.75 0 0 1 1.06.04Zm3.44 1.06a.75.75 0 1 1 1.02-1.1l3.5 3.25a.75.75 0 0 1 0 1.1l-3.5 3.25a.75.75 0 1 1-1.02-1.1l2.908-2.7-2.908-2.7Z"></path><path d="M1.5 4.25c0-.966.784-1.75 1.75-1.75h17.5c.966 0 1.75.784 1.75 1.75v12.5a1.75 1.75 0 0 1-1.75 1.75h-9.69l-3.573 3.573A1.458 1.458 0 0 1 5 21.043V18.5H3.25a1.75 1.75 0 0 1-1.75-1.75ZM3.25 4a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h2.5a.75.75 0 0 1 .75.75v3.19l3.72-3.72a.749.749 0 0 1 .53-.22h10a.25.25 0 0 0 .25-.25V4.25a.25.25 0 0 0-.25-.25Z"></path> </svg> <div> <div class="color-fg-default h4">Code Review</div> Manage code changes </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"discussions","context":"product","tag":"link","label":"discussions_link_product_navbar"}" href="https://github.com/features/discussions"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-comment-discussion color-fg-subtle mr-3"> <path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.458 1.458 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25v-9.5C0 1.784.784 1 1.75 1ZM1.5 2.75v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Z"></path><path d="M22.5 8.75a.25.25 0 0 0-.25-.25h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5a.75.75 0 0 1 .53.22l2.72 2.72v-2.19a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5Z"></path> </svg> <div> <div class="color-fg-default h4">Discussions</div> Collaborate outside of code </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"code_search","context":"product","tag":"link","label":"code_search_link_product_navbar"}" href="https://github.com/features/code-search"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-code-square color-fg-subtle mr-3"> <path d="M10.3 8.24a.75.75 0 0 1-.04 1.06L7.352 12l2.908 2.7a.75.75 0 1 1-1.02 1.1l-3.5-3.25a.75.75 0 0 1 0-1.1l3.5-3.25a.75.75 0 0 1 1.06.04Zm3.44 1.06a.75.75 0 1 1 1.02-1.1l3.5 3.25a.75.75 0 0 1 0 1.1l-3.5 3.25a.75.75 0 1 1-1.02-1.1l2.908-2.7-2.908-2.7Z"></path><path d="M2 3.75C2 2.784 2.784 2 3.75 2h16.5c.966 0 1.75.784 1.75 1.75v16.5A1.75 1.75 0 0 1 20.25 22H3.75A1.75 1.75 0 0 1 2 20.25Zm1.75-.25a.25.25 0 0 0-.25.25v16.5c0 .138.112.25.25.25h16.5a.25.25 0 0 0 .25-.25V3.75a.25.25 0 0 0-.25-.25Z"></path> </svg> <div> <div class="color-fg-default h4">Code Search</div> Find more, search less </div> </a></li> </ul> </div> </div> <div class="HeaderMenu-column px-lg-4"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0 border-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="product-explore-heading">Explore</span> <ul class="list-style-none f5" aria-labelledby="product-explore-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"all_features","context":"product","tag":"link","label":"all_features_link_product_navbar"}" href="https://github.com/features"> All features </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"documentation","context":"product","tag":"link","label":"documentation_link_product_navbar"}" href="https://docs.github.com"> Documentation <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"github_skills","context":"product","tag":"link","label":"github_skills_link_product_navbar"}" href="https://skills.github.com"> GitHub Skills <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"blog","context":"product","tag":"link","label":"blog_link_product_navbar"}" href="https://github.blog"> Blog <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> </ul> </div> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Solutions <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 d-lg-flex flex-wrap dropdown-menu-wide"> <div class="HeaderMenu-column px-lg-4 border-lg-right mb-4 mb-lg-0 pr-lg-7"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0 pb-lg-3 mb-3 mb-lg-0"> <span class="d-block h4 color-fg-default my-1" id="solutions-by-company-size-heading">By company size</span> <ul class="list-style-none f5" aria-labelledby="solutions-by-company-size-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"enterprises","context":"solutions","tag":"link","label":"enterprises_link_solutions_navbar"}" href="https://github.com/enterprise"> Enterprises </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"small_and_medium_teams","context":"solutions","tag":"link","label":"small_and_medium_teams_link_solutions_navbar"}" href="https://github.com/team"> Small and medium teams </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"startups","context":"solutions","tag":"link","label":"startups_link_solutions_navbar"}" href="https://github.com/enterprise/startups"> Startups </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"nonprofits","context":"solutions","tag":"link","label":"nonprofits_link_solutions_navbar"}" href="/solutions/industry/nonprofits"> Nonprofits </a></li> </ul> </div> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="solutions-by-use-case-heading">By use case</span> <ul class="list-style-none f5" aria-labelledby="solutions-by-use-case-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"devsecops","context":"solutions","tag":"link","label":"devsecops_link_solutions_navbar"}" href="/solutions/use-case/devsecops"> DevSecOps </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"devops","context":"solutions","tag":"link","label":"devops_link_solutions_navbar"}" href="/solutions/use-case/devops"> DevOps </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"ci_cd","context":"solutions","tag":"link","label":"ci_cd_link_solutions_navbar"}" href="/solutions/use-case/ci-cd"> CI/CD </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"view_all_use_cases","context":"solutions","tag":"link","label":"view_all_use_cases_link_solutions_navbar"}" href="/solutions/use-case"> View all use cases </a></li> </ul> </div> </div> <div class="HeaderMenu-column px-lg-4"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="solutions-by-industry-heading">By industry</span> <ul class="list-style-none f5" aria-labelledby="solutions-by-industry-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"healthcare","context":"solutions","tag":"link","label":"healthcare_link_solutions_navbar"}" href="/solutions/industry/healthcare"> Healthcare </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"financial_services","context":"solutions","tag":"link","label":"financial_services_link_solutions_navbar"}" href="/solutions/industry/financial-services"> Financial services </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"manufacturing","context":"solutions","tag":"link","label":"manufacturing_link_solutions_navbar"}" href="/solutions/industry/manufacturing"> Manufacturing </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"government","context":"solutions","tag":"link","label":"government_link_solutions_navbar"}" href="/solutions/industry/government"> Government </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"view_all_industries","context":"solutions","tag":"link","label":"view_all_industries_link_solutions_navbar"}" href="/solutions/industry"> View all industries </a></li> </ul> </div> </div> <div class="HeaderMenu-trailing-link rounded-bottom-2 flex-shrink-0 mt-lg-4 px-lg-4 py-4 py-lg-3 f5 text-semibold"> <a href="/solutions"> View all solutions <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-right HeaderMenu-trailing-link-icon"> <path d="M6.22 3.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L9.94 8 6.22 4.28a.75.75 0 0 1 0-1.06Z"></path> </svg> </a> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Resources <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 pb-2 pb-lg-4 d-lg-flex flex-wrap dropdown-menu-wide"> <div class="HeaderMenu-column px-lg-4 border-lg-right mb-4 mb-lg-0 pr-lg-7"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="resources-topics-heading">Topics</span> <ul class="list-style-none f5" aria-labelledby="resources-topics-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"ai","context":"resources","tag":"link","label":"ai_link_resources_navbar"}" href="/resources/articles/ai"> AI </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"devops","context":"resources","tag":"link","label":"devops_link_resources_navbar"}" href="/resources/articles/devops"> DevOps </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"security","context":"resources","tag":"link","label":"security_link_resources_navbar"}" href="/resources/articles/security"> Security </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"software_development","context":"resources","tag":"link","label":"software_development_link_resources_navbar"}" href="/resources/articles/software-development"> Software Development </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"view_all","context":"resources","tag":"link","label":"view_all_link_resources_navbar"}" href="/resources/articles"> View all </a></li> </ul> </div> </div> <div class="HeaderMenu-column px-lg-4"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0 border-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="resources-explore-heading">Explore</span> <ul class="list-style-none f5" aria-labelledby="resources-explore-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"learning_pathways","context":"resources","tag":"link","label":"learning_pathways_link_resources_navbar"}" href="https://resources.github.com/learn/pathways"> Learning Pathways <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"events_amp_webinars","context":"resources","tag":"link","label":"events_amp_webinars_link_resources_navbar"}" href="https://resources.github.com"> Events & Webinars <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"ebooks_amp_whitepapers","context":"resources","tag":"link","label":"ebooks_amp_whitepapers_link_resources_navbar"}" href="https://github.com/resources/whitepapers"> Ebooks & Whitepapers </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"customer_stories","context":"resources","tag":"link","label":"customer_stories_link_resources_navbar"}" href="https://github.com/customer-stories"> Customer Stories </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"partners","context":"resources","tag":"link","label":"partners_link_resources_navbar"}" href="https://partner.github.com"> Partners <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"executive_insights","context":"resources","tag":"link","label":"executive_insights_link_resources_navbar"}" href="https://github.com/solutions/executive-insights"> Executive Insights </a></li> </ul> </div> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Open Source <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 pb-2 pb-lg-4 px-lg-4"> <div class="HeaderMenu-column"> <div class="border-bottom pb-3 pb-lg-0 pb-lg-3 mb-3 mb-lg-0 mb-lg-3"> <ul class="list-style-none f5" > <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"github_sponsors","context":"open_source","tag":"link","label":"github_sponsors_link_open_source_navbar"}" href="/sponsors"> <div> <div class="color-fg-default h4">GitHub Sponsors</div> Fund open source developers </div> </a></li> </ul> </div> <div class="border-bottom pb-3 pb-lg-0 pb-lg-3 mb-3 mb-lg-0 mb-lg-3"> <ul class="list-style-none f5" > <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"the_readme_project","context":"open_source","tag":"link","label":"the_readme_project_link_open_source_navbar"}" href="https://github.com/readme"> <div> <div class="color-fg-default h4">The ReadME Project</div> GitHub community articles </div> </a></li> </ul> </div> <div class="border-bottom pb-3 pb-lg-0 border-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="open-source-repositories-heading">Repositories</span> <ul class="list-style-none f5" aria-labelledby="open-source-repositories-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"topics","context":"open_source","tag":"link","label":"topics_link_open_source_navbar"}" href="https://github.com/topics"> Topics </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"trending","context":"open_source","tag":"link","label":"trending_link_open_source_navbar"}" href="https://github.com/trending"> Trending </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"collections","context":"open_source","tag":"link","label":"collections_link_open_source_navbar"}" href="https://github.com/collections"> Collections </a></li> </ul> </div> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Enterprise <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 pb-2 pb-lg-4 px-lg-4"> <div class="HeaderMenu-column"> <div class="border-bottom pb-3 pb-lg-0 pb-lg-3 mb-3 mb-lg-0 mb-lg-3"> <ul class="list-style-none f5" > <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"enterprise_platform","context":"enterprise","tag":"link","label":"enterprise_platform_link_enterprise_navbar"}" href="/enterprise"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-stack color-fg-subtle mr-3"> <path d="M11.063 1.456a1.749 1.749 0 0 1 1.874 0l8.383 5.316a1.751 1.751 0 0 1 0 2.956l-8.383 5.316a1.749 1.749 0 0 1-1.874 0L2.68 9.728a1.751 1.751 0 0 1 0-2.956Zm1.071 1.267a.25.25 0 0 0-.268 0L3.483 8.039a.25.25 0 0 0 0 .422l8.383 5.316a.25.25 0 0 0 .268 0l8.383-5.316a.25.25 0 0 0 0-.422Z"></path><path d="M1.867 12.324a.75.75 0 0 1 1.035-.232l8.964 5.685a.25.25 0 0 0 .268 0l8.964-5.685a.75.75 0 0 1 .804 1.267l-8.965 5.685a1.749 1.749 0 0 1-1.874 0l-8.965-5.685a.75.75 0 0 1-.231-1.035Z"></path><path d="M1.867 16.324a.75.75 0 0 1 1.035-.232l8.964 5.685a.25.25 0 0 0 .268 0l8.964-5.685a.75.75 0 0 1 .804 1.267l-8.965 5.685a1.749 1.749 0 0 1-1.874 0l-8.965-5.685a.75.75 0 0 1-.231-1.035Z"></path> </svg> <div> <div class="color-fg-default h4">Enterprise platform</div> AI-powered developer platform </div> </a></li> </ul> </div> <div class="border-bottom pb-3 pb-lg-0 border-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="enterprise-available-add-ons-heading">Available add-ons</span> <ul class="list-style-none f5" aria-labelledby="enterprise-available-add-ons-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"github_advanced_security","context":"enterprise","tag":"link","label":"github_advanced_security_link_enterprise_navbar"}" href="https://github.com/security/advanced-security"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-shield-check color-fg-subtle mr-3"> <path d="M16.53 9.78a.75.75 0 0 0-1.06-1.06L11 13.19l-1.97-1.97a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l5-5Z"></path><path d="m12.54.637 8.25 2.675A1.75 1.75 0 0 1 22 4.976V10c0 6.19-3.771 10.704-9.401 12.83a1.704 1.704 0 0 1-1.198 0C5.77 20.705 2 16.19 2 10V4.976c0-.758.489-1.43 1.21-1.664L11.46.637a1.748 1.748 0 0 1 1.08 0Zm-.617 1.426-8.25 2.676a.249.249 0 0 0-.173.237V10c0 5.46 3.28 9.483 8.43 11.426a.199.199 0 0 0 .14 0C17.22 19.483 20.5 15.461 20.5 10V4.976a.25.25 0 0 0-.173-.237l-8.25-2.676a.253.253 0 0 0-.154 0Z"></path> </svg> <div> <div class="color-fg-default h4">GitHub Advanced Security</div> Enterprise-grade security features </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"copilot_for_business","context":"enterprise","tag":"link","label":"copilot_for_business_link_enterprise_navbar"}" href="/features/copilot/copilot-business"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-copilot color-fg-subtle mr-3"> <path d="M23.922 16.992c-.861 1.495-5.859 5.023-11.922 5.023-6.063 0-11.061-3.528-11.922-5.023A.641.641 0 0 1 0 16.736v-2.869a.841.841 0 0 1 .053-.22c.372-.935 1.347-2.292 2.605-2.656.167-.429.414-1.055.644-1.517a10.195 10.195 0 0 1-.052-1.086c0-1.331.282-2.499 1.132-3.368.397-.406.89-.717 1.474-.952 1.399-1.136 3.392-2.093 6.122-2.093 2.731 0 4.767.957 6.166 2.093.584.235 1.077.546 1.474.952.85.869 1.132 2.037 1.132 3.368 0 .368-.014.733-.052 1.086.23.462.477 1.088.644 1.517 1.258.364 2.233 1.721 2.605 2.656a.832.832 0 0 1 .053.22v2.869a.641.641 0 0 1-.078.256ZM12.172 11h-.344a4.323 4.323 0 0 1-.355.508C10.703 12.455 9.555 13 7.965 13c-1.725 0-2.989-.359-3.782-1.259a2.005 2.005 0 0 1-.085-.104L4 11.741v6.585c1.435.779 4.514 2.179 8 2.179 3.486 0 6.565-1.4 8-2.179v-6.585l-.098-.104s-.033.045-.085.104c-.793.9-2.057 1.259-3.782 1.259-1.59 0-2.738-.545-3.508-1.492a4.323 4.323 0 0 1-.355-.508h-.016.016Zm.641-2.935c.136 1.057.403 1.913.878 2.497.442.544 1.134.938 2.344.938 1.573 0 2.292-.337 2.657-.751.384-.435.558-1.15.558-2.361 0-1.14-.243-1.847-.705-2.319-.477-.488-1.319-.862-2.824-1.025-1.487-.161-2.192.138-2.533.529-.269.307-.437.808-.438 1.578v.021c0 .265.021.562.063.893Zm-1.626 0c.042-.331.063-.628.063-.894v-.02c-.001-.77-.169-1.271-.438-1.578-.341-.391-1.046-.69-2.533-.529-1.505.163-2.347.537-2.824 1.025-.462.472-.705 1.179-.705 2.319 0 1.211.175 1.926.558 2.361.365.414 1.084.751 2.657.751 1.21 0 1.902-.394 2.344-.938.475-.584.742-1.44.878-2.497Z"></path><path d="M14.5 14.25a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1Zm-5 0a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1Z"></path> </svg> <div> <div class="color-fg-default h4">Copilot for business</div> Enterprise-grade AI features </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"premium_support","context":"enterprise","tag":"link","label":"premium_support_link_enterprise_navbar"}" href="/premium-support"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-comment-discussion color-fg-subtle mr-3"> <path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.458 1.458 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25v-9.5C0 1.784.784 1 1.75 1ZM1.5 2.75v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Z"></path><path d="M22.5 8.75a.25.25 0 0 0-.25-.25h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5a.75.75 0 0 1 .53.22l2.72 2.72v-2.19a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5Z"></path> </svg> <div> <div class="color-fg-default h4">Premium Support</div> Enterprise-grade 24/7 support </div> </a></li> </ul> </div> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <a class="HeaderMenu-link no-underline px-0 px-lg-2 py-3 py-lg-2 d-block d-lg-inline-block" data-analytics-event="{"location":"navbar","action":"pricing","context":"global","tag":"link","label":"pricing_link_global_navbar"}" href="https://github.com/pricing">Pricing</a> </li> </ul> </nav> <div class="d-flex flex-column flex-lg-row width-full flex-justify-end flex-lg-items-center text-center mt-3 mt-lg-0 text-lg-left ml-lg-3"> <qbsearch-input class="search-input" data-scope="repo:tomitribe/crest" data-custom-scopes-path="/search/custom_scopes" data-delete-custom-scopes-csrf="cqkY4LOozj7t4J7UP7la0zuIBZD43brPzKUkiBqFtlHmdQwfy0BDhaKRxxw4hDTVGtqq78Nna0cZzSdM916kow" data-max-custom-scopes="10" data-header-redesign-enabled="false" data-initial-value="" data-blackbird-suggestions-path="/search/suggestions" data-jump-to-suggestions-path="/_graphql/GetSuggestedNavigationDestinations" data-current-repository="tomitribe/crest" data-current-org="tomitribe" data-current-owner="" data-logged-in="false" data-copilot-chat-enabled="false" data-nl-search-enabled="false" data-retain-scroll-position="true"> <div class="search-input-container search-with-dialog position-relative d-flex flex-row flex-items-center mr-4 rounded" data-action="click:qbsearch-input#searchInputContainerClicked" > <button type="button" class="header-search-button placeholder input-button form-control d-flex flex-1 flex-self-stretch flex-items-center no-wrap width-full py-0 pl-2 pr-0 text-left border-0 box-shadow-none" data-target="qbsearch-input.inputButton" aria-label="Search or jump to…" aria-haspopup="dialog" placeholder="Search or jump to..." data-hotkey=s,/ autocapitalize="off" data-analytics-event="{"location":"navbar","action":"searchbar","context":"global","tag":"input","label":"searchbar_input_global_navbar"}" data-action="click:qbsearch-input#handleExpand" > <div class="mr-2 color-fg-muted"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-search"> <path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path> </svg> </div> <span class="flex-1" data-target="qbsearch-input.inputButtonText">Search or jump to...</span> <div class="d-flex" data-target="qbsearch-input.hotkeyIndicator"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="20" aria-hidden="true" class="mr-1"><path fill="none" stroke="#979A9C" opacity=".4" d="M3.5.5h12c1.7 0 3 1.3 3 3v13c0 1.7-1.3 3-3 3h-12c-1.7 0-3-1.3-3-3v-13c0-1.7 1.3-3 3-3z"></path><path fill="#979A9C" d="M11.8 6L8 15.1h-.9L10.8 6h1z"></path></svg> </div> </button> <input type="hidden" name="type" class="js-site-search-type-field"> <div class="Overlay--hidden " data-modal-dialog-overlay> <modal-dialog data-action="close:qbsearch-input#handleClose cancel:qbsearch-input#handleClose" data-target="qbsearch-input.searchSuggestionsDialog" role="dialog" id="search-suggestions-dialog" aria-modal="true" aria-labelledby="search-suggestions-dialog-header" data-view-component="true" class="Overlay Overlay--width-large Overlay--height-auto"> <h1 id="search-suggestions-dialog-header" class="sr-only">Search code, repositories, users, issues, pull requests...</h1> <div class="Overlay-body Overlay-body--paddingNone"> <div data-view-component="true"> <div class="search-suggestions position-fixed width-full color-shadow-large border color-fg-default color-bg-default overflow-hidden d-flex flex-column query-builder-container" style="border-radius: 12px;" data-target="qbsearch-input.queryBuilderContainer" hidden > <!-- '"` --><!-- </textarea></xmp> --></option></form><form id="query-builder-test-form" action="" accept-charset="UTF-8" method="get"> <query-builder data-target="qbsearch-input.queryBuilder" id="query-builder-query-builder-test" data-filter-key=":" data-view-component="true" class="QueryBuilder search-query-builder"> <div class="FormControl FormControl--fullWidth"> <label id="query-builder-test-label" for="query-builder-test" class="FormControl-label sr-only"> Search </label> <div class="QueryBuilder-StyledInput width-fit " data-target="query-builder.styledInput" > <span id="query-builder-test-leadingvisual-wrap" class="FormControl-input-leadingVisualWrap QueryBuilder-leadingVisualWrap"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-search FormControl-input-leadingVisual"> <path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path> </svg> </span> <div data-target="query-builder.styledInputContainer" class="QueryBuilder-StyledInputContainer"> <div aria-hidden="true" class="QueryBuilder-StyledInputContent" data-target="query-builder.styledInputContent" ></div> <div class="QueryBuilder-InputWrapper"> <div aria-hidden="true" class="QueryBuilder-Sizer" data-target="query-builder.sizer"></div> <input id="query-builder-test" name="query-builder-test" value="" autocomplete="off" type="text" role="combobox" spellcheck="false" aria-expanded="false" aria-describedby="validation-2a4454a6-b289-44e7-8467-74091e2effe3" data-target="query-builder.input" data-action=" input:query-builder#inputChange blur:query-builder#inputBlur keydown:query-builder#inputKeydown focus:query-builder#inputFocus " data-view-component="true" class="FormControl-input QueryBuilder-Input FormControl-medium" /> </div> </div> <span class="sr-only" id="query-builder-test-clear">Clear</span> <button role="button" id="query-builder-test-clear-button" aria-labelledby="query-builder-test-clear query-builder-test-label" data-target="query-builder.clearButton" data-action=" click:query-builder#clear focus:query-builder#clearButtonFocus blur:query-builder#clearButtonBlur " variant="small" hidden="hidden" type="button" data-view-component="true" class="Button Button--iconOnly Button--invisible Button--medium mr-1 px-2 py-0 d-flex flex-items-center rounded-1 color-fg-muted"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x-circle-fill Button-visual"> <path d="M2.343 13.657A8 8 0 1 1 13.658 2.343 8 8 0 0 1 2.343 13.657ZM6.03 4.97a.751.751 0 0 0-1.042.018.751.751 0 0 0-.018 1.042L6.94 8 4.97 9.97a.749.749 0 0 0 .326 1.275.749.749 0 0 0 .734-.215L8 9.06l1.97 1.97a.749.749 0 0 0 1.275-.326.749.749 0 0 0-.215-.734L9.06 8l1.97-1.97a.749.749 0 0 0-.326-1.275.749.749 0 0 0-.734.215L8 6.94Z"></path> </svg> </button> </div> <template id="search-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-search"> <path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path> </svg> </template> <template id="code-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code"> <path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path> </svg> </template> <template id="file-code-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-file-code"> <path d="M4 1.75C4 .784 4.784 0 5.75 0h5.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v8.586A1.75 1.75 0 0 1 14.25 15h-9a.75.75 0 0 1 0-1.5h9a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 10 4.25V1.5H5.75a.25.25 0 0 0-.25.25v2.5a.75.75 0 0 1-1.5 0Zm1.72 4.97a.75.75 0 0 1 1.06 0l2 2a.75.75 0 0 1 0 1.06l-2 2a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734l1.47-1.47-1.47-1.47a.75.75 0 0 1 0-1.06ZM3.28 7.78 1.81 9.25l1.47 1.47a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018l-2-2a.75.75 0 0 1 0-1.06l2-2a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Zm8.22-6.218V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path> </svg> </template> <template id="history-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-history"> <path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path> </svg> </template> <template id="repo-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo"> <path d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8ZM5 12.25a.25.25 0 0 1 .25-.25h3.5a.25.25 0 0 1 .25.25v3.25a.25.25 0 0 1-.4.2l-1.45-1.087a.249.249 0 0 0-.3 0L5.4 15.7a.25.25 0 0 1-.4-.2Z"></path> </svg> </template> <template id="bookmark-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-bookmark"> <path d="M3 2.75C3 1.784 3.784 1 4.75 1h6.5c.966 0 1.75.784 1.75 1.75v11.5a.75.75 0 0 1-1.227.579L8 11.722l-3.773 3.107A.751.751 0 0 1 3 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.91l3.023-2.489a.75.75 0 0 1 .954 0l3.023 2.49V2.75a.25.25 0 0 0-.25-.25Z"></path> </svg> </template> <template id="plus-circle-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-plus-circle"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm7.25-3.25v2.5h2.5a.75.75 0 0 1 0 1.5h-2.5v2.5a.75.75 0 0 1-1.5 0v-2.5h-2.5a.75.75 0 0 1 0-1.5h2.5v-2.5a.75.75 0 0 1 1.5 0Z"></path> </svg> </template> <template id="circle-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-dot-fill"> <path d="M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z"></path> </svg> </template> <template id="trash-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-trash"> <path d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"></path> </svg> </template> <template id="team-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-people"> <path d="M2 5.5a3.5 3.5 0 1 1 5.898 2.549 5.508 5.508 0 0 1 3.034 4.084.75.75 0 1 1-1.482.235 4 4 0 0 0-7.9 0 .75.75 0 0 1-1.482-.236A5.507 5.507 0 0 1 3.102 8.05 3.493 3.493 0 0 1 2 5.5ZM11 4a3.001 3.001 0 0 1 2.22 5.018 5.01 5.01 0 0 1 2.56 3.012.749.749 0 0 1-.885.954.752.752 0 0 1-.549-.514 3.507 3.507 0 0 0-2.522-2.372.75.75 0 0 1-.574-.73v-.352a.75.75 0 0 1 .416-.672A1.5 1.5 0 0 0 11 5.5.75.75 0 0 1 11 4Zm-5.5-.5a2 2 0 1 0-.001 3.999A2 2 0 0 0 5.5 3.5Z"></path> </svg> </template> <template id="project-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-project"> <path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0ZM1.5 1.75v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25ZM11.75 3a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-1.5 0v-7.5a.75.75 0 0 1 .75-.75Zm-8.25.75a.75.75 0 0 1 1.5 0v5.5a.75.75 0 0 1-1.5 0ZM8 3a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 3Z"></path> </svg> </template> <template id="pencil-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-pencil"> <path d="M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 0 1-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.61Zm.176 4.823L9.75 4.81l-6.286 6.287a.253.253 0 0 0-.064.108l-.558 1.953 1.953-.558a.253.253 0 0 0 .108-.064Zm1.238-3.763a.25.25 0 0 0-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 0 0 0-.354Z"></path> </svg> </template> <template id="copilot-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copilot"> <path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path> </svg> </template> <template id="copilot-error-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copilot-error"> <path d="M16 11.24c0 .112-.072.274-.21.467L13 9.688V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-.198 0-.388-.009-.571-.029L6.833 5.226a4.01 4.01 0 0 0 .17-.782c.117-.935-.037-1.395-.241-1.614-.193-.206-.637-.413-1.682-.297-.683.076-1.115.231-1.395.415l-1.257-.91c.579-.564 1.413-.877 2.485-.996 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095Zm-5.083-8.707c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Zm2.511 11.074c-1.393.776-3.272 1.428-5.43 1.428-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.18-.455-.241-.963-.252-1.475L.31 4.107A.747.747 0 0 1 0 3.509V3.49a.748.748 0 0 1 .625-.73c.156-.026.306.047.435.139l14.667 10.578a.592.592 0 0 1 .227.264.752.752 0 0 1 .046.249v.022a.75.75 0 0 1-1.19.596Zm-1.367-.991L5.635 7.964a5.128 5.128 0 0 1-.889.073c-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433 1.539 0 3.089-.505 4.063-.934Z"></path> </svg> </template> <template id="workflow-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-workflow"> <path d="M0 1.75C0 .784.784 0 1.75 0h3.5C6.216 0 7 .784 7 1.75v3.5A1.75 1.75 0 0 1 5.25 7H4v4a1 1 0 0 0 1 1h4v-1.25C9 9.784 9.784 9 10.75 9h3.5c.966 0 1.75.784 1.75 1.75v3.5A1.75 1.75 0 0 1 14.25 16h-3.5A1.75 1.75 0 0 1 9 14.25v-.75H5A2.5 2.5 0 0 1 2.5 11V7h-.75A1.75 1.75 0 0 1 0 5.25Zm1.75-.25a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Zm9 9a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Z"></path> </svg> </template> <template id="book-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book"> <path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path> </svg> </template> <template id="code-review-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code-review"> <path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 13H8.061l-2.574 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25v-8.5C0 1.784.784 1 1.75 1ZM1.5 2.75v8.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Zm5.28 1.72a.75.75 0 0 1 0 1.06L5.31 7l1.47 1.47a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018l-2-2a.75.75 0 0 1 0-1.06l2-2a.75.75 0 0 1 1.06 0Zm2.44 0a.75.75 0 0 1 1.06 0l2 2a.75.75 0 0 1 0 1.06l-2 2a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L10.69 7 9.22 5.53a.75.75 0 0 1 0-1.06Z"></path> </svg> </template> <template id="codespaces-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-codespaces"> <path d="M0 11.25c0-.966.784-1.75 1.75-1.75h12.5c.966 0 1.75.784 1.75 1.75v3A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm2-9.5C2 .784 2.784 0 3.75 0h8.5C13.216 0 14 .784 14 1.75v5a1.75 1.75 0 0 1-1.75 1.75h-8.5A1.75 1.75 0 0 1 2 6.75Zm1.75-.25a.25.25 0 0 0-.25.25v5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-5a.25.25 0 0 0-.25-.25Zm-2 9.5a.25.25 0 0 0-.25.25v3c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-3a.25.25 0 0 0-.25-.25Z"></path><path d="M7 12.75a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75Zm-4 0a.75.75 0 0 1 .75-.75h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1-.75-.75Z"></path> </svg> </template> <template id="comment-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-comment"> <path d="M1 2.75C1 1.784 1.784 1 2.75 1h10.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 13.25 12H9.06l-2.573 2.573A1.458 1.458 0 0 1 4 13.543V12H2.75A1.75 1.75 0 0 1 1 10.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h4.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> </svg> </template> <template id="comment-discussion-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-comment-discussion"> <path d="M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z"></path> </svg> </template> <template id="organization-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-organization"> <path d="M1.75 16A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0h8.5C11.216 0 12 .784 12 1.75v12.5c0 .085-.006.168-.018.25h2.268a.25.25 0 0 0 .25-.25V8.285a.25.25 0 0 0-.111-.208l-1.055-.703a.749.749 0 1 1 .832-1.248l1.055.703c.487.325.779.871.779 1.456v5.965A1.75 1.75 0 0 1 14.25 16h-3.5a.766.766 0 0 1-.197-.026c-.099.017-.2.026-.303.026h-3a.75.75 0 0 1-.75-.75V14h-1v1.25a.75.75 0 0 1-.75.75Zm-.25-1.75c0 .138.112.25.25.25H4v-1.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 .75.75v1.25h2.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25ZM3.75 6h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5ZM3 3.75A.75.75 0 0 1 3.75 3h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 3.75Zm4 3A.75.75 0 0 1 7.75 6h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 7 6.75ZM7.75 3h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5ZM3 9.75A.75.75 0 0 1 3.75 9h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 9.75ZM7.75 9h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5Z"></path> </svg> </template> <template id="rocket-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-rocket"> <path d="M14.064 0h.186C15.216 0 16 .784 16 1.75v.186a8.752 8.752 0 0 1-2.564 6.186l-.458.459c-.314.314-.641.616-.979.904v3.207c0 .608-.315 1.172-.833 1.49l-2.774 1.707a.749.749 0 0 1-1.11-.418l-.954-3.102a1.214 1.214 0 0 1-.145-.125L3.754 9.816a1.218 1.218 0 0 1-.124-.145L.528 8.717a.749.749 0 0 1-.418-1.11l1.71-2.774A1.748 1.748 0 0 1 3.31 4h3.204c.288-.338.59-.665.904-.979l.459-.458A8.749 8.749 0 0 1 14.064 0ZM8.938 3.623h-.002l-.458.458c-.76.76-1.437 1.598-2.02 2.5l-1.5 2.317 2.143 2.143 2.317-1.5c.902-.583 1.74-1.26 2.499-2.02l.459-.458a7.25 7.25 0 0 0 2.123-5.127V1.75a.25.25 0 0 0-.25-.25h-.186a7.249 7.249 0 0 0-5.125 2.123ZM3.56 14.56c-.732.732-2.334 1.045-3.005 1.148a.234.234 0 0 1-.201-.064.234.234 0 0 1-.064-.201c.103-.671.416-2.273 1.15-3.003a1.502 1.502 0 1 1 2.12 2.12Zm6.94-3.935c-.088.06-.177.118-.266.175l-2.35 1.521.548 1.783 1.949-1.2a.25.25 0 0 0 .119-.213ZM3.678 8.116 5.2 5.766c.058-.09.117-.178.176-.266H3.309a.25.25 0 0 0-.213.119l-1.2 1.95ZM12 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> </template> <template id="shield-check-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-shield-check"> <path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path> </svg> </template> <template id="heart-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-heart"> <path d="m8 14.25.345.666a.75.75 0 0 1-.69 0l-.008-.004-.018-.01a7.152 7.152 0 0 1-.31-.17 22.055 22.055 0 0 1-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.066 22.066 0 0 1-3.744 2.584l-.018.01-.006.003h-.002ZM4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.58 20.58 0 0 0 8 13.393a20.58 20.58 0 0 0 3.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.749.749 0 0 1-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5Z"></path> </svg> </template> <template id="server-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-server"> <path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v4c0 .372-.116.717-.314 1 .198.283.314.628.314 1v4a1.75 1.75 0 0 1-1.75 1.75H1.75A1.75 1.75 0 0 1 0 12.75v-4c0-.358.109-.707.314-1a1.739 1.739 0 0 1-.314-1v-4C0 1.784.784 1 1.75 1ZM1.5 2.75v4c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-4a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Zm.25 5.75a.25.25 0 0 0-.25.25v4c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-4a.25.25 0 0 0-.25-.25ZM7 4.75A.75.75 0 0 1 7.75 4h4.5a.75.75 0 0 1 0 1.5h-4.5A.75.75 0 0 1 7 4.75ZM7.75 10h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM3 4.75A.75.75 0 0 1 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 4.75ZM3.75 10h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5Z"></path> </svg> </template> <template id="globe-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-globe"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM5.78 8.75a9.64 9.64 0 0 0 1.363 4.177c.255.426.542.832.857 1.215.245-.296.551-.705.857-1.215A9.64 9.64 0 0 0 10.22 8.75Zm4.44-1.5a9.64 9.64 0 0 0-1.363-4.177c-.307-.51-.612-.919-.857-1.215a9.927 9.927 0 0 0-.857 1.215A9.64 9.64 0 0 0 5.78 7.25Zm-5.944 1.5H1.543a6.507 6.507 0 0 0 4.666 5.5c-.123-.181-.24-.365-.352-.552-.715-1.192-1.437-2.874-1.581-4.948Zm-2.733-1.5h2.733c.144-2.074.866-3.756 1.58-4.948.12-.197.237-.381.353-.552a6.507 6.507 0 0 0-4.666 5.5Zm10.181 1.5c-.144 2.074-.866 3.756-1.58 4.948-.12.197-.237.381-.353.552a6.507 6.507 0 0 0 4.666-5.5Zm2.733-1.5a6.507 6.507 0 0 0-4.666-5.5c.123.181.24.365.353.552.714 1.192 1.436 2.874 1.58 4.948Z"></path> </svg> </template> <template id="issue-opened-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-issue-opened"> <path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path> </svg> </template> <template id="device-mobile-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-device-mobile"> <path d="M3.75 0h8.5C13.216 0 14 .784 14 1.75v12.5A1.75 1.75 0 0 1 12.25 16h-8.5A1.75 1.75 0 0 1 2 14.25V1.75C2 .784 2.784 0 3.75 0ZM3.5 1.75v12.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25ZM8 13a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path> </svg> </template> <template id="package-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-package"> <path d="m8.878.392 5.25 3.045c.54.314.872.89.872 1.514v6.098a1.75 1.75 0 0 1-.872 1.514l-5.25 3.045a1.75 1.75 0 0 1-1.756 0l-5.25-3.045A1.75 1.75 0 0 1 1 11.049V4.951c0-.624.332-1.201.872-1.514L7.122.392a1.75 1.75 0 0 1 1.756 0ZM7.875 1.69l-4.63 2.685L8 7.133l4.755-2.758-4.63-2.685a.248.248 0 0 0-.25 0ZM2.5 5.677v5.372c0 .09.047.171.125.216l4.625 2.683V8.432Zm6.25 8.271 4.625-2.683a.25.25 0 0 0 .125-.216V5.677L8.75 8.432Z"></path> </svg> </template> <template id="credit-card-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-credit-card"> <path d="M10.75 9a.75.75 0 0 0 0 1.5h1.5a.75.75 0 0 0 0-1.5h-1.5Z"></path><path d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 14H1.75A1.75 1.75 0 0 1 0 12.25ZM14.5 6.5h-13v5.75c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25Zm0-2.75a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25V5h13Z"></path> </svg> </template> <template id="play-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-play"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path> </svg> </template> <template id="gift-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-gift"> <path d="M2 2.75A2.75 2.75 0 0 1 4.75 0c.983 0 1.873.42 2.57 1.232.268.318.497.668.68 1.042.183-.375.411-.725.68-1.044C9.376.42 10.266 0 11.25 0a2.75 2.75 0 0 1 2.45 4h.55c.966 0 1.75.784 1.75 1.75v2c0 .698-.409 1.301-1 1.582v4.918A1.75 1.75 0 0 1 13.25 16H2.75A1.75 1.75 0 0 1 1 14.25V9.332C.409 9.05 0 8.448 0 7.75v-2C0 4.784.784 4 1.75 4h.55c-.192-.375-.3-.8-.3-1.25ZM7.25 9.5H2.5v4.75c0 .138.112.25.25.25h4.5Zm1.5 0v5h4.5a.25.25 0 0 0 .25-.25V9.5Zm0-4V8h5.5a.25.25 0 0 0 .25-.25v-2a.25.25 0 0 0-.25-.25Zm-7 0a.25.25 0 0 0-.25.25v2c0 .138.112.25.25.25h5.5V5.5h-5.5Zm3-4a1.25 1.25 0 0 0 0 2.5h2.309c-.233-.818-.542-1.401-.878-1.793-.43-.502-.915-.707-1.431-.707ZM8.941 4h2.309a1.25 1.25 0 0 0 0-2.5c-.516 0-1 .205-1.43.707-.337.392-.646.975-.879 1.793Z"></path> </svg> </template> <template id="code-square-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code-square"> <path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25Zm7.47 3.97a.75.75 0 0 1 1.06 0l2 2a.75.75 0 0 1 0 1.06l-2 2a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L10.69 8 9.22 6.53a.75.75 0 0 1 0-1.06ZM6.78 6.53 5.31 8l1.47 1.47a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-2-2a.75.75 0 0 1 0-1.06l2-2a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path> </svg> </template> <template id="device-desktop-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-device-desktop"> <path d="M14.25 1c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 14.25 12h-3.727c.099 1.041.52 1.872 1.292 2.757A.752.752 0 0 1 11.25 16h-6.5a.75.75 0 0 1-.565-1.243c.772-.885 1.192-1.716 1.292-2.757H1.75A1.75 1.75 0 0 1 0 10.25v-7.5C0 1.784.784 1 1.75 1ZM1.75 2.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25ZM9.018 12H6.982a5.72 5.72 0 0 1-.765 2.5h3.566a5.72 5.72 0 0 1-.765-2.5Z"></path> </svg> </template> <div class="position-relative"> <ul role="listbox" class="ActionListWrap QueryBuilder-ListWrap" aria-label="Suggestions" data-action=" combobox-commit:query-builder#comboboxCommit mousedown:query-builder#resultsMousedown " data-target="query-builder.resultsList" data-persist-list=false id="query-builder-test-results" ></ul> </div> <div class="FormControl-inlineValidation" id="validation-2a4454a6-b289-44e7-8467-74091e2effe3" hidden="hidden"> <span class="FormControl-inlineValidation--visual"> <svg aria-hidden="true" height="12" viewBox="0 0 12 12" version="1.1" width="12" data-view-component="true" class="octicon octicon-alert-fill"> <path d="M4.855.708c.5-.896 1.79-.896 2.29 0l4.675 8.351a1.312 1.312 0 0 1-1.146 1.954H1.33A1.313 1.313 0 0 1 .183 9.058ZM7 7V3H5v4Zm-1 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"></path> </svg> </span> <span></span> </div> </div> <div data-target="query-builder.screenReaderFeedback" aria-live="polite" aria-atomic="true" class="sr-only"></div> </query-builder></form> <div class="d-flex flex-row color-fg-muted px-3 text-small color-bg-default search-feedback-prompt"> <a target="_blank" href="https://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax" data-view-component="true" class="Link color-fg-accent text-normal ml-2">Search syntax tips</a> <div class="d-flex flex-1"></div> </div> </div> </div> </div> </modal-dialog></div> </div> <div data-action="click:qbsearch-input#retract" class="dark-backdrop position-fixed" hidden data-target="qbsearch-input.darkBackdrop"></div> <div class="color-fg-default"> <dialog-helper> <dialog data-target="qbsearch-input.feedbackDialog" data-action="close:qbsearch-input#handleDialogClose cancel:qbsearch-input#handleDialogClose" id="feedback-dialog" aria-modal="true" aria-labelledby="feedback-dialog-title" aria-describedby="feedback-dialog-description" data-view-component="true" class="Overlay Overlay-whenNarrow Overlay--size-medium Overlay--motion-scaleFade Overlay--disableScroll"> <div data-view-component="true" class="Overlay-header"> <div class="Overlay-headerContentWrap"> <div class="Overlay-titleWrap"> <h1 class="Overlay-title " id="feedback-dialog-title"> Provide feedback </h1> </div> <div class="Overlay-actionWrap"> <button data-close-dialog-id="feedback-dialog" aria-label="Close" type="button" data-view-component="true" class="close-button Overlay-closeButton"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg></button> </div> </div> </div> <scrollable-region data-labelled-by="feedback-dialog-title"> <div data-view-component="true" class="Overlay-body"> <!-- '"` --><!-- </textarea></xmp> --></option></form><form id="code-search-feedback-form" data-turbo="false" action="/search/feedback" accept-charset="UTF-8" method="post"><input type="hidden" data-csrf="true" name="authenticity_token" value="HZYHtzU1EQmea1iRIgYZWqzoPybrO2OiO2w5jKFfw0lh5LIVxREzAsZtOEtlwEktkchoo9bYdkGf+siAS/oPcQ==" /> <p>We read every piece of feedback, and take your input very seriously.</p> <textarea name="feedback" class="form-control width-full mb-2" style="height: 120px" id="feedback"></textarea> <input name="include_email" id="include_email" aria-label="Include my email address so I can be contacted" class="form-control mr-2" type="checkbox"> <label for="include_email" style="font-weight: normal">Include my email address so I can be contacted</label> </form></div> </scrollable-region> <div data-view-component="true" class="Overlay-footer Overlay-footer--alignEnd"> <button data-close-dialog-id="feedback-dialog" type="button" data-view-component="true" class="btn"> Cancel </button> <button form="code-search-feedback-form" data-action="click:qbsearch-input#submitFeedback" type="submit" data-view-component="true" class="btn-primary btn"> Submit feedback </button> </div> </dialog></dialog-helper> <custom-scopes data-target="qbsearch-input.customScopesManager"> <dialog-helper> <dialog data-target="custom-scopes.customScopesModalDialog" data-action="close:qbsearch-input#handleDialogClose cancel:qbsearch-input#handleDialogClose" id="custom-scopes-dialog" aria-modal="true" aria-labelledby="custom-scopes-dialog-title" aria-describedby="custom-scopes-dialog-description" data-view-component="true" class="Overlay Overlay-whenNarrow Overlay--size-medium Overlay--motion-scaleFade Overlay--disableScroll"> <div data-view-component="true" class="Overlay-header Overlay-header--divided"> <div class="Overlay-headerContentWrap"> <div class="Overlay-titleWrap"> <h1 class="Overlay-title " id="custom-scopes-dialog-title"> Saved searches </h1> <h2 id="custom-scopes-dialog-description" class="Overlay-description">Use saved searches to filter your results more quickly</h2> </div> <div class="Overlay-actionWrap"> <button data-close-dialog-id="custom-scopes-dialog" aria-label="Close" type="button" data-view-component="true" class="close-button Overlay-closeButton"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg></button> </div> </div> </div> <scrollable-region data-labelled-by="custom-scopes-dialog-title"> <div data-view-component="true" class="Overlay-body"> <div data-target="custom-scopes.customScopesModalDialogFlash"></div> <div hidden class="create-custom-scope-form" data-target="custom-scopes.createCustomScopeForm"> <!-- '"` --><!-- </textarea></xmp> --></option></form><form id="custom-scopes-dialog-form" data-turbo="false" action="/search/custom_scopes" accept-charset="UTF-8" method="post"><input type="hidden" data-csrf="true" name="authenticity_token" value="d6CP/LCvF79Wbpvdg4PrsD8POMfp4dUkD3LORkorT9ZIIjJzkUUt5Smc8arYgpHt20nuiHkue0nuHfRC9hmQyA==" /> <div data-target="custom-scopes.customScopesModalDialogFlash"></div> <input type="hidden" id="custom_scope_id" name="custom_scope_id" data-target="custom-scopes.customScopesIdField"> <div class="form-group"> <label for="custom_scope_name">Name</label> <auto-check src="/search/custom_scopes/check_name" required only-validate-on-blur="false"> <input type="text" name="custom_scope_name" id="custom_scope_name" data-target="custom-scopes.customScopesNameField" class="form-control" autocomplete="off" placeholder="github-ruby" required maxlength="50"> <input type="hidden" data-csrf="true" value="/Tlx/TDcK8w8CziNWQhpNWvJoq0o/jFtolbp6XViUmEiJLxjfL80W5fq1R4Dite3RSMbG1urXuMaG5D+uZQapw==" /> </auto-check> </div> <div class="form-group"> <label for="custom_scope_query">Query</label> <input type="text" name="custom_scope_query" id="custom_scope_query" data-target="custom-scopes.customScopesQueryField" class="form-control" autocomplete="off" placeholder="(repo:mona/a OR repo:mona/b) AND lang:python" required maxlength="500"> </div> <p class="text-small color-fg-muted"> To see all available qualifiers, see our <a class="Link--inTextBlock" href="https://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax">documentation</a>. </p> </form> </div> <div data-target="custom-scopes.manageCustomScopesForm"> <div data-target="custom-scopes.list"></div> </div> </div> </scrollable-region> <div data-view-component="true" class="Overlay-footer Overlay-footer--alignEnd Overlay-footer--divided"> <button data-action="click:custom-scopes#customScopesCancel" type="button" data-view-component="true" class="btn"> Cancel </button> <button form="custom-scopes-dialog-form" data-action="click:custom-scopes#customScopesSubmit" data-target="custom-scopes.customScopesSubmitButton" type="submit" data-view-component="true" class="btn-primary btn"> Create saved search </button> </div> </dialog></dialog-helper> </custom-scopes> </div> </qbsearch-input> <div class="position-relative HeaderMenu-link-wrap d-lg-inline-block"> <a href="/login?return_to=https%3A%2F%2Fgithub.com%2Ftomitribe%2Fcrest" class="HeaderMenu-link HeaderMenu-link--sign-in HeaderMenu-button flex-shrink-0 no-underline d-none d-lg-inline-flex border border-lg-0 rounded rounded-lg-0 px-2 py-1" style="margin-left: 12px;" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/tomitribe/crest","user_id":null}}" data-hydro-click-hmac="85a7b0fed772de6cb6cc65108d060ef226fafb4358d588211ca909ae4f50ac36" data-analytics-event="{"category":"Marketing nav","action":"click to go to homepage","label":"ref_page:Marketing;ref_cta:Sign in;ref_loc:Header"}" > Sign in </a> </div> <a href="/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E&source=header-repo&source_repo=tomitribe%2Fcrest" class="HeaderMenu-link HeaderMenu-link--sign-up HeaderMenu-button flex-shrink-0 d-flex d-lg-inline-flex no-underline border color-border-default rounded px-2 py-1" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/tomitribe/crest","user_id":null}}" data-hydro-click-hmac="85a7b0fed772de6cb6cc65108d060ef226fafb4358d588211ca909ae4f50ac36" data-analytics-event="{"category":"Sign up","action":"click to sign up for account","label":"ref_page:/<user-name>/<repo-name>;ref_cta:Sign up;ref_loc:header logged out"}" > Sign up </a> <button type="button" class="sr-only js-header-menu-focus-trap d-block d-lg-none">Reseting focus</button> </div> </div> </div> </div> </header> <div hidden="hidden" data-view-component="true" class="js-stale-session-flash stale-session-flash flash flash-warn flash-full"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert"> <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> <span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span> <span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span> <span class="js-stale-session-flash-switched" hidden>You switched accounts on another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span> <button id="icon-button-64a2ed35-c055-4fc9-95dd-a2b9e120f9f0" aria-labelledby="tooltip-dab8a6a5-fb85-459e-8717-36932c981018" type="button" data-view-component="true" class="Button Button--iconOnly Button--invisible Button--medium flash-close js-flash-close"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x Button-visual"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg> </button><tool-tip id="tooltip-dab8a6a5-fb85-459e-8717-36932c981018" for="icon-button-64a2ed35-c055-4fc9-95dd-a2b9e120f9f0" popover="manual" data-direction="s" data-type="label" data-view-component="true" class="sr-only position-absolute">Dismiss alert</tool-tip> </div> </div> <div id="start-of-content" class="show-on-focus"></div> <div id="js-flash-container" class="flash-container" data-turbo-replace> <template class="js-flash-template"> <div class="flash flash-full {{ className }}"> <div > <button autofocus class="flash-close js-flash-close" type="button" aria-label="Dismiss this message"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg> </button> <div aria-atomic="true" role="alert" class="js-flash-alert"> <div>{{ message }}</div> </div> </div> </div> </template> </div> <div class="application-main " data-commit-hovercards-enabled data-discussion-hovercards-enabled data-issue-and-pr-hovercards-enabled data-project-hovercards-enabled > <div itemscope itemtype="http://schema.org/SoftwareSourceCode" class=""> <main id="js-repo-pjax-container" > <div id="repository-container-header" class="pt-3 hide-full-screen" style="background-color: var(--page-header-bgColor, var(--color-page-header-bg));" data-turbo-replace> <div class="d-flex flex-nowrap flex-justify-end mb-3 px-3 px-lg-5" style="gap: 1rem;"> <div class="flex-auto min-width-0 width-fit"> <div class=" d-flex flex-wrap flex-items-center wb-break-word f3 text-normal"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo color-fg-muted mr-2"> <path d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8ZM5 12.25a.25.25 0 0 1 .25-.25h3.5a.25.25 0 0 1 .25.25v3.25a.25.25 0 0 1-.4.2l-1.45-1.087a.249.249 0 0 0-.3 0L5.4 15.7a.25.25 0 0 1-.4-.2Z"></path> </svg> <span class="author flex-self-stretch" itemprop="author"> <a class="url fn" rel="author" data-hovercard-type="organization" data-hovercard-url="/orgs/tomitribe/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/tomitribe"> tomitribe </a> </span> <span class="mx-1 flex-self-stretch color-fg-muted">/</span> <strong itemprop="name" class="mr-2 flex-self-stretch"> <a data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" href="/tomitribe/crest">crest</a> </strong> <span></span><span class="Label Label--secondary v-align-middle mr-1">Public</span> </div> </div> <div id="repository-details-container" class="flex-shrink-0" data-turbo-replace style="max-width: 70%;"> <ul class="pagehead-actions flex-shrink-0 d-none d-md-inline" style="padding: 2px 0;"> <li> <a href="/login?return_to=%2Ftomitribe%2Fcrest" rel="nofollow" id="repository-details-watch-button" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"notification subscription menu watch","repository_id":null,"auth_type":"LOG_IN","originating_url":"https://github.com/tomitribe/crest","user_id":null}}" data-hydro-click-hmac="d0c72d1d3928597443a2d959218b73af5f4f1a1040e584244bd6052689e2fe28" aria-label="You must be signed in to change notification settings" data-view-component="true" class="btn-sm btn"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-bell mr-2"> <path d="M8 16a2 2 0 0 0 1.985-1.75c.017-.137-.097-.25-.235-.25h-3.5c-.138 0-.252.113-.235.25A2 2 0 0 0 8 16ZM3 5a5 5 0 0 1 10 0v2.947c0 .05.015.098.042.139l1.703 2.555A1.519 1.519 0 0 1 13.482 13H2.518a1.516 1.516 0 0 1-1.263-2.36l1.703-2.554A.255.255 0 0 0 3 7.947Zm5-3.5A3.5 3.5 0 0 0 4.5 5v2.947c0 .346-.102.683-.294.97l-1.703 2.556a.017.017 0 0 0-.003.01l.001.006c0 .002.002.004.004.006l.006.004.007.001h10.964l.007-.001.006-.004.004-.006.001-.007a.017.017 0 0 0-.003-.01l-1.703-2.554a1.745 1.745 0 0 1-.294-.97V5A3.5 3.5 0 0 0 8 1.5Z"></path> </svg>Notifications </a> <tool-tip id="tooltip-c5709ddc-3c9a-4406-9fbf-ee03a346474e" for="repository-details-watch-button" popover="manual" data-direction="s" data-type="description" data-view-component="true" class="sr-only position-absolute">You must be signed in to change notification settings</tool-tip> </li> <li> <a icon="repo-forked" id="fork-button" href="/login?return_to=%2Ftomitribe%2Fcrest" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"repo details fork button","repository_id":11673895,"auth_type":"LOG_IN","originating_url":"https://github.com/tomitribe/crest","user_id":null}}" data-hydro-click-hmac="b947de27e728d79dbe13073b801b0adfc725980c9d37bab9425fd4b18ad11368" data-view-component="true" class="btn-sm btn"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo-forked mr-2"> <path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"></path> </svg>Fork <span id="repo-network-counter" data-pjax-replace="true" data-turbo-replace="true" title="18" data-view-component="true" class="Counter">18</span> </a> </li> <li> <div data-view-component="true" class="BtnGroup d-flex"> <a href="/login?return_to=%2Ftomitribe%2Fcrest" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"star button","repository_id":11673895,"auth_type":"LOG_IN","originating_url":"https://github.com/tomitribe/crest","user_id":null}}" data-hydro-click-hmac="358d31a502102fc3d6ef80ac0ab3b35b27aa9adab0667796f3441f5ad9264a0d" aria-label="You must be signed in to star a repository" data-view-component="true" class="tooltipped tooltipped-sw btn-sm btn"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star v-align-text-bottom d-inline-block mr-2"> <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path> </svg><span data-view-component="true" class="d-inline"> Star </span> <span id="repo-stars-counter-star" aria-label="57 users starred this repository" data-singular-suffix="user starred this repository" data-plural-suffix="users starred this repository" data-turbo-replace="true" title="57" data-view-component="true" class="Counter js-social-count">57</span> </a></div> </li> </ul> </div> </div> <div id="responsive-meta-container" data-turbo-replace> <div class="d-block d-md-none mb-2 px-3 px-md-4 px-lg-5"> <p class="f4 mb-3 "> Command-line API modeled after JAX-RS (Command REST) </p> <h3 class="sr-only">License</h3> <div class="mb-2"> <a href="/tomitribe/crest/blob/master/LICENSE" class="Link--muted" data-analytics-event="{"category":"Repository Overview","action":"click","label":"location:sidebar;file:license"}" > <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-law mr-2"> <path d="M8.75.75V2h.985c.304 0 .603.08.867.231l1.29.736c.038.022.08.033.124.033h2.234a.75.75 0 0 1 0 1.5h-.427l2.111 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.006.005-.01.01-.045.04c-.21.176-.441.327-.686.45C14.556 10.78 13.88 11 13 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L12.178 4.5h-.162c-.305 0-.604-.079-.868-.231l-1.29-.736a.245.245 0 0 0-.124-.033H8.75V13h2.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.5V3.5h-.984a.245.245 0 0 0-.124.033l-1.289.737c-.265.15-.564.23-.869.23h-.162l2.112 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.016.015-.045.04c-.21.176-.441.327-.686.45C4.556 10.78 3.88 11 3 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L2.178 4.5H1.75a.75.75 0 0 1 0-1.5h2.234a.249.249 0 0 0 .125-.033l1.288-.737c.265-.15.564-.23.869-.23h.984V.75a.75.75 0 0 1 1.5 0Zm2.945 8.477c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327Zm-10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327Z"></path> </svg> Apache-2.0 license </a> </div> <div class="mb-3"> <a class="Link--secondary no-underline mr-3" href="/tomitribe/crest/stargazers"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star mr-1"> <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path> </svg> <span class="text-bold">57</span> stars </a> <a class="Link--secondary no-underline mr-3" href="/tomitribe/crest/forks"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo-forked mr-1"> <path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"></path> </svg> <span class="text-bold">18</span> forks </a> <a class="Link--secondary no-underline mr-3 d-inline-block" href="/tomitribe/crest/branches"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-git-branch mr-1"> <path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path> </svg> <span>Branches</span> </a> <a class="Link--secondary no-underline d-inline-block" href="/tomitribe/crest/tags"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-tag mr-1"> <path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path> </svg> <span>Tags</span> </a> <a class="Link--secondary no-underline d-inline-block" href="/tomitribe/crest/activity"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-pulse mr-1"> <path d="M6 2c.306 0 .582.187.696.471L10 10.731l1.304-3.26A.751.751 0 0 1 12 7h3.25a.75.75 0 0 1 0 1.5h-2.742l-1.812 4.528a.751.751 0 0 1-1.392 0L6 4.77 4.696 8.03A.75.75 0 0 1 4 8.5H.75a.75.75 0 0 1 0-1.5h2.742l1.812-4.529A.751.751 0 0 1 6 2Z"></path> </svg> <span>Activity</span> </a> </div> <div class="d-flex flex-wrap gap-2"> <div class="flex-1"> <div data-view-component="true" class="BtnGroup d-flex"> <a href="/login?return_to=%2Ftomitribe%2Fcrest" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"star button","repository_id":11673895,"auth_type":"LOG_IN","originating_url":"https://github.com/tomitribe/crest","user_id":null}}" data-hydro-click-hmac="358d31a502102fc3d6ef80ac0ab3b35b27aa9adab0667796f3441f5ad9264a0d" aria-label="You must be signed in to star a repository" data-view-component="true" class="tooltipped tooltipped-sw btn-sm btn btn-block"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star v-align-text-bottom d-inline-block mr-2"> <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path> </svg><span data-view-component="true" class="d-inline"> Star </span> </a></div> </div> <div class="flex-1"> <a href="/login?return_to=%2Ftomitribe%2Fcrest" rel="nofollow" id="files-overview-watch-button" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"notification subscription menu watch","repository_id":null,"auth_type":"LOG_IN","originating_url":"https://github.com/tomitribe/crest","user_id":null}}" data-hydro-click-hmac="d0c72d1d3928597443a2d959218b73af5f4f1a1040e584244bd6052689e2fe28" aria-label="You must be signed in to change notification settings" data-view-component="true" class="btn-sm btn btn-block"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-bell mr-2"> <path d="M8 16a2 2 0 0 0 1.985-1.75c.017-.137-.097-.25-.235-.25h-3.5c-.138 0-.252.113-.235.25A2 2 0 0 0 8 16ZM3 5a5 5 0 0 1 10 0v2.947c0 .05.015.098.042.139l1.703 2.555A1.519 1.519 0 0 1 13.482 13H2.518a1.516 1.516 0 0 1-1.263-2.36l1.703-2.554A.255.255 0 0 0 3 7.947Zm5-3.5A3.5 3.5 0 0 0 4.5 5v2.947c0 .346-.102.683-.294.97l-1.703 2.556a.017.017 0 0 0-.003.01l.001.006c0 .002.002.004.004.006l.006.004.007.001h10.964l.007-.001.006-.004.004-.006.001-.007a.017.017 0 0 0-.003-.01l-1.703-2.554a1.745 1.745 0 0 1-.294-.97V5A3.5 3.5 0 0 0 8 1.5Z"></path> </svg>Notifications </a> <tool-tip id="tooltip-162a70c2-db51-491f-8bb9-6cdfce399fe4" for="files-overview-watch-button" popover="manual" data-direction="s" data-type="description" data-view-component="true" class="sr-only position-absolute">You must be signed in to change notification settings</tool-tip> </div> <span> </span> </div> </div> </div> <nav data-pjax="#js-repo-pjax-container" aria-label="Repository" data-view-component="true" class="js-repo-nav js-sidenav-container-pjax js-responsive-underlinenav overflow-hidden UnderlineNav px-3 px-md-4 px-lg-5"> <ul data-view-component="true" class="UnderlineNav-body list-style-none"> <li data-view-component="true" class="d-inline-flex"> <a id="code-tab" href="/tomitribe/crest" data-tab-item="i0code-tab" data-selected-links="repo_source repo_downloads repo_commits repo_releases repo_tags repo_branches repo_packages repo_deployments repo_attestations /tomitribe/crest" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g c" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Code","target":"UNDERLINE_NAV.TAB"}" aria-current="page" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item selected"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code UnderlineNav-octicon d-none d-sm-inline"> <path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path> </svg> <span data-content="Code">Code</span> <span id="code-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="Not available" data-view-component="true" class="Counter"></span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="issues-tab" href="/tomitribe/crest/issues" data-tab-item="i1issues-tab" data-selected-links="repo_issues repo_labels repo_milestones /tomitribe/crest/issues" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g i" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Issues","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-issue-opened UnderlineNav-octicon d-none d-sm-inline"> <path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path> </svg> <span data-content="Issues">Issues</span> <span id="issues-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="28" data-view-component="true" class="Counter">28</span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="pull-requests-tab" href="/tomitribe/crest/pulls" data-tab-item="i2pull-requests-tab" data-selected-links="repo_pulls checks /tomitribe/crest/pulls" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g p" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Pull requests","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-git-pull-request UnderlineNav-octicon d-none d-sm-inline"> <path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"></path> </svg> <span data-content="Pull requests">Pull requests</span> <span id="pull-requests-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="1" data-view-component="true" class="Counter">1</span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="actions-tab" href="/tomitribe/crest/actions" data-tab-item="i3actions-tab" data-selected-links="repo_actions /tomitribe/crest/actions" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g a" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Actions","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-play UnderlineNav-octicon d-none d-sm-inline"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path> </svg> <span data-content="Actions">Actions</span> <span id="actions-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="Not available" data-view-component="true" class="Counter"></span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="projects-tab" href="/tomitribe/crest/projects" data-tab-item="i4projects-tab" data-selected-links="repo_projects new_repo_project repo_project /tomitribe/crest/projects" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g b" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Projects","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-table UnderlineNav-octicon d-none d-sm-inline"> <path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25ZM6.5 6.5v8h7.75a.25.25 0 0 0 .25-.25V6.5Zm8-1.5V1.75a.25.25 0 0 0-.25-.25H6.5V5Zm-13 1.5v7.75c0 .138.112.25.25.25H5v-8ZM5 5V1.5H1.75a.25.25 0 0 0-.25.25V5Z"></path> </svg> <span data-content="Projects">Projects</span> <span id="projects-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="0" hidden="hidden" data-view-component="true" class="Counter">0</span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="wiki-tab" href="/tomitribe/crest/wiki" data-tab-item="i5wiki-tab" data-selected-links="repo_wiki /tomitribe/crest/wiki" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g w" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Wiki","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book UnderlineNav-octicon d-none d-sm-inline"> <path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path> </svg> <span data-content="Wiki">Wiki</span> <span id="wiki-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="Not available" data-view-component="true" class="Counter"></span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="security-tab" href="/tomitribe/crest/security" data-tab-item="i6security-tab" data-selected-links="security overview alerts policy token_scanning code_scanning /tomitribe/crest/security" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g s" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Security","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-shield UnderlineNav-octicon d-none d-sm-inline"> <path d="M7.467.133a1.748 1.748 0 0 1 1.066 0l5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667Zm.61 1.429a.25.25 0 0 0-.153 0l-5.25 1.68a.25.25 0 0 0-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.196.196 0 0 0 .154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.251.251 0 0 0-.174-.237l-5.25-1.68ZM8.75 4.75v3a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 1.5 0ZM9 10.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> <span data-content="Security">Security</span> <include-fragment src="/tomitribe/crest/security/overall-count" accept="text/fragment+html"></include-fragment> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="insights-tab" href="/tomitribe/crest/pulse" data-tab-item="i7insights-tab" data-selected-links="repo_graphs repo_contributors dependency_graph dependabot_updates pulse people community /tomitribe/crest/pulse" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Insights","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-graph UnderlineNav-octicon d-none d-sm-inline"> <path d="M1.5 1.75V13.5h13.75a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1-.75-.75V1.75a.75.75 0 0 1 1.5 0Zm14.28 2.53-5.25 5.25a.75.75 0 0 1-1.06 0L7 7.06 4.28 9.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.25-3.25a.75.75 0 0 1 1.06 0L10 7.94l4.72-4.72a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path> </svg> <span data-content="Insights">Insights</span> <span id="insights-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="Not available" data-view-component="true" class="Counter"></span> </a></li> </ul> <div style="visibility:hidden;" data-view-component="true" class="UnderlineNav-actions js-responsive-underlinenav-overflow position-absolute pr-3 pr-md-4 pr-lg-5 right-0"> <action-menu data-select-variant="none" data-view-component="true"> <focus-group direction="vertical" mnemonics retain> <button id="action-menu-886e71c6-3cfd-4146-96f1-74f3db4b4053-button" popovertarget="action-menu-886e71c6-3cfd-4146-96f1-74f3db4b4053-overlay" aria-controls="action-menu-886e71c6-3cfd-4146-96f1-74f3db4b4053-list" aria-haspopup="true" aria-labelledby="tooltip-7c8c68dd-756f-4b2d-a70b-b9944f45351a" type="button" data-view-component="true" class="Button Button--iconOnly Button--secondary Button--medium UnderlineNav-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-kebab-horizontal Button-visual"> <path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path> </svg> </button><tool-tip id="tooltip-7c8c68dd-756f-4b2d-a70b-b9944f45351a" for="action-menu-886e71c6-3cfd-4146-96f1-74f3db4b4053-button" popover="manual" data-direction="s" data-type="label" data-view-component="true" class="sr-only position-absolute">Additional navigation options</tool-tip> <anchored-position data-target="action-menu.overlay" id="action-menu-886e71c6-3cfd-4146-96f1-74f3db4b4053-overlay" anchor="action-menu-886e71c6-3cfd-4146-96f1-74f3db4b4053-button" align="start" side="outside-bottom" anchor-offset="normal" popover="auto" data-view-component="true"> <div data-view-component="true" class="Overlay Overlay--size-auto"> <div data-view-component="true" class="Overlay-body Overlay-body--paddingNone"> <action-list> <div data-view-component="true"> <ul aria-labelledby="action-menu-886e71c6-3cfd-4146-96f1-74f3db4b4053-button" id="action-menu-886e71c6-3cfd-4146-96f1-74f3db4b4053-list" role="menu" data-view-component="true" class="ActionListWrap--inset ActionListWrap"> <li hidden="hidden" data-menu-item="i0code-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-f8c8a2f1-cafe-4378-afc6-a331832da1f8" href="/tomitribe/crest" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code"> <path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Code </span> </a> </li> <li hidden="hidden" data-menu-item="i1issues-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-87e36db4-033b-4eec-9af6-728c089dcc2d" href="/tomitribe/crest/issues" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-issue-opened"> <path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Issues </span> </a> </li> <li hidden="hidden" data-menu-item="i2pull-requests-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-f09ffb38-9e0e-46c8-b40c-d81b29685b46" href="/tomitribe/crest/pulls" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-git-pull-request"> <path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Pull requests </span> </a> </li> <li hidden="hidden" data-menu-item="i3actions-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-2d4cb428-1bd1-4a8b-a901-d37452b9f2c8" href="/tomitribe/crest/actions" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-play"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Actions </span> </a> </li> <li hidden="hidden" data-menu-item="i4projects-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-e14e6568-379f-47de-bf3f-9944f3c109eb" href="/tomitribe/crest/projects" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-table"> <path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25ZM6.5 6.5v8h7.75a.25.25 0 0 0 .25-.25V6.5Zm8-1.5V1.75a.25.25 0 0 0-.25-.25H6.5V5Zm-13 1.5v7.75c0 .138.112.25.25.25H5v-8ZM5 5V1.5H1.75a.25.25 0 0 0-.25.25V5Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Projects </span> </a> </li> <li hidden="hidden" data-menu-item="i5wiki-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-3431287d-384f-4bce-a489-a91cb06f895e" href="/tomitribe/crest/wiki" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book"> <path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Wiki </span> </a> </li> <li hidden="hidden" data-menu-item="i6security-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-615d1da8-ed98-4035-885c-49d89c54be1a" href="/tomitribe/crest/security" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-shield"> <path d="M7.467.133a1.748 1.748 0 0 1 1.066 0l5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667Zm.61 1.429a.25.25 0 0 0-.153 0l-5.25 1.68a.25.25 0 0 0-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.196.196 0 0 0 .154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.251.251 0 0 0-.174-.237l-5.25-1.68ZM8.75 4.75v3a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 1.5 0ZM9 10.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Security </span> </a> </li> <li hidden="hidden" data-menu-item="i7insights-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-aa662dde-21e5-4bea-8d48-8439d66caa15" href="/tomitribe/crest/pulse" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-graph"> <path d="M1.5 1.75V13.5h13.75a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1-.75-.75V1.75a.75.75 0 0 1 1.5 0Zm14.28 2.53-5.25 5.25a.75.75 0 0 1-1.06 0L7 7.06 4.28 9.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.25-3.25a.75.75 0 0 1 1.06 0L10 7.94l4.72-4.72a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Insights </span> </a> </li> </ul> </div></action-list> </div> </div></anchored-position> </focus-group> </action-menu></div> </nav> </div> <turbo-frame id="repo-content-turbo-frame" target="_top" data-turbo-action="advance" class=""> <div id="repo-content-pjax-container" class="repository-content " > <h1 class='sr-only'>tomitribe/crest</h1> <div class="clearfix container-xl px-md-4 px-lg-5 px-3"> <div> <div style="max-width: 100%" data-view-component="true" class="Layout Layout--flowRow-until-md react-repos-overview-margin Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end"> <div data-view-component="true" class="Layout-main"> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_dompurify_dist_purify_es_mjs-dd1d3ea6a436.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_tanstack_query-core_build_modern_queryObserver_js-node_modules_tanstack_-defd52-843b41414e0e.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_aria-live_aria-live_ts-ui_packages_history_history_ts-ui_packages_promise-with-re-01dc80-b13b6c1d97b0.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_paths_index_ts-8a20a6d3af54.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_ref-selector_RefSelector_tsx-7496afc3784d.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_commit-attribution_index_ts-ui_packages_commit-checks-status_index_ts-ui_packages-762eaa-bac5b6fc3f70.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_code-view-shared_hooks_use-canonical-object_ts-ui_packages_code-view-shared_hooks-c2dbff-923a12b3d018.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/repos-overview-60297eccc5de.js"></script> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react.a490b7c9fa319e5cb069.module.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/repos-overview.0ee7cac3ab511a65d9f9.module.css" /> <react-partial partial-name="repos-overview" data-ssr="true" data-attempted-ssr="true" > <script type="application/json" data-target="react-partial.embeddedData">{"props":{"initialPayload":{"allShortcutsEnabled":false,"path":"/","repo":{"id":11673895,"defaultBranch":"master","name":"crest","ownerLogin":"tomitribe","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2013-07-26T00:12:26.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/2865265?v=4","public":true,"private":false,"isOrgOwned":true},"currentUser":null,"refInfo":{"name":"master","listCacheKey":"v0:1742437648.0","canEdit":false,"refType":"branch","currentOid":"5d9d2084d3544c626a18023a55bc6219f2abf0de"},"tree":{"items":[{"name":"crest-maven-plugin","path":"crest-maven-plugin","contentType":"directory"},{"name":"tomitribe-crest-api","path":"tomitribe-crest-api","contentType":"directory"},{"name":"tomitribe-crest-archetype","path":"tomitribe-crest-archetype","contentType":"directory"},{"name":"tomitribe-crest-arthur-extension","path":"tomitribe-crest-arthur-extension","contentType":"directory"},{"name":"tomitribe-crest-cli","path":"tomitribe-crest-cli","contentType":"directory"},{"name":"tomitribe-crest-generator","path":"tomitribe-crest-generator","contentType":"directory"},{"name":"tomitribe-crest-test","path":"tomitribe-crest-test","contentType":"directory"},{"name":"tomitribe-crest-xbean","path":"tomitribe-crest-xbean","contentType":"directory"},{"name":"tomitribe-crest","path":"tomitribe-crest","contentType":"directory"},{"name":"toolz","path":"toolz","contentType":"directory"},{"name":".drone.yml","path":".drone.yml","contentType":"file"},{"name":".gitignore","path":".gitignore","contentType":"file"},{"name":".travis.yml","path":".travis.yml","contentType":"file"},{"name":"LICENSE","path":"LICENSE","contentType":"file"},{"name":"README.adoc","path":"README.adoc","contentType":"file"},{"name":"pom.xml","path":"pom.xml","contentType":"file"}],"templateDirectorySuggestionUrl":null,"readme":null,"totalCount":16,"showBranchInfobar":false},"fileTree":null,"fileTreeProcessingTime":null,"foldersToFetch":[],"treeExpanded":false,"symbolsExpanded":false,"isOverview":true,"overview":{"banners":{"shouldRecommendReadme":false,"isPersonalRepo":false,"showUseActionBanner":false,"actionSlug":null,"actionId":null,"showProtectBranchBanner":false,"publishBannersInfo":{"dismissActionNoticePath":"/settings/dismiss-notice/publish_action_from_repo","releasePath":"/tomitribe/crest/releases/new?marketplace=true","showPublishActionBanner":false},"interactionLimitBanner":null,"showInvitationBanner":false,"inviterName":null,"actionsMigrationBannerInfo":{"releaseTags":[],"showImmutableActionsMigrationBanner":false,"initialMigrationStatus":null}},"codeButton":{"contactPath":"/contact","isEnterprise":false,"local":{"protocolInfo":{"httpAvailable":true,"sshAvailable":null,"httpUrl":"https://github.com/tomitribe/crest.git","showCloneWarning":null,"sshUrl":null,"sshCertificatesRequired":null,"sshCertificatesAvailable":null,"ghCliUrl":"gh repo clone tomitribe/crest","defaultProtocol":"http","newSshKeyUrl":"/settings/ssh/new","setProtocolPath":"/users/set_protocol"},"platformInfo":{"cloneUrl":"https://desktop.github.com","showVisualStudioCloneButton":false,"visualStudioCloneUrl":"https://windows.github.com","showXcodeCloneButton":false,"xcodeCloneUrl":"xcode://clone?repo=https%3A%2F%2Fgithub.com%2Ftomitribe%2Fcrest","zipballUrl":"/tomitribe/crest/archive/refs/heads/master.zip"}},"newCodespacePath":"/codespaces/new?hide_repo_select=true\u0026repo=11673895"},"popovers":{"rename":null,"renamedParentRepo":null},"commitCount":"496","overviewFiles":[{"displayName":"README.adoc","repoName":"crest","refName":"master","path":"README.adoc","preferredFileType":"readme","tabName":"README","richText":"\u003carticle class=\"markdown-body entry-content container-lg\" itemprop=\"text\"\u003e\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eCREST\u003c/h1\u003e\u003ca id=\"user-content-crest\" class=\"anchor\" aria-label=\"Permalink: CREST\" href=\"#crest\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv id=\"user-content-preamble\" dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eCommand-line API styled after JAX-RS\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eCREST allows you to get to the real work as quickly as possible when writing command line tools in Java.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e100% annotation based\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eUse Bean Validation or custom validators on use input\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eContains Several builtin validations\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eGenerates help from annotations\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eSupports default values\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eUse variable substitution on defaults\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eSupports lists and var-ags\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eSupports any java type, usually out of the box\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eSimply annotate the parameters of any Java method so it can be invoked from a command-line interface\n with near-zero additional work. Command-registration, help text and validation is taken care of for you.\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-start-your-project\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eStart your project\u003c/h2\u003e\u003ca id=\"user-content-start-your-project\" class=\"anchor\" aria-label=\"Permalink: Start your project\" href=\"#start-your-project\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eUse the Maven archetype and run your first command now. Copy the following commands and paste them into your terminal.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003emvn archetype:generate -DarchetypeGroupId=org.tomitribe -DarchetypeArtifactId=tomitribe-crest-archetype -DarchetypeVersion=0.22 -DgroupId=org.example -DartifactId=mycommand\ncd mycommand/\nmvn clean install\n./target/mycommand hello\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf all went well you should see the following output:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003eHello, World!\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eYes, you can actually create executable command-line programs in Java!\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-example-rsync-as-a-crest-command\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eExample: rsync as a Crest command\u003c/h2\u003e\u003ca id=\"user-content-example-rsync-as-a-crest-command\" class=\"anchor\" aria-label=\"Permalink: Example: rsync as a Crest command\" href=\"#example-rsync-as-a-crest-command\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eFor example, to do something that might be similar to rsync in java, you could create the following\nmethod signature in any java object.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"package org.example.toolz;\n\nimport org.tomitribe.crest.api.Command;\nimport org.tomitribe.crest.api.Default;\nimport org.tomitribe.crest.api.Option;\n\nimport java.io.File;\nimport java.net.URI;\nimport java.util.regex.Pattern;\n\npublic class AnyName {\n\n @Command\n public void rsync(@Option(\u0026quot;recursive\u0026quot;) boolean recursive,\n @Option(\u0026quot;links\u0026quot;) boolean links,\n @Option(\u0026quot;perms\u0026quot;) boolean perms,\n @Option(\u0026quot;owner\u0026quot;) boolean owner,\n @Option(\u0026quot;group\u0026quot;) boolean group,\n @Option(\u0026quot;devices\u0026quot;) boolean devices,\n @Option(\u0026quot;specials\u0026quot;) boolean specials,\n @Option(\u0026quot;times\u0026quot;) boolean times,\n @Option(\u0026quot;exclude\u0026quot;) Pattern exclude,\n @Option(\u0026quot;exclude-from\u0026quot;) File excludeFrom,\n @Option(\u0026quot;include\u0026quot;) Pattern include,\n @Option(\u0026quot;include-from\u0026quot;) File includeFrom,\n @Option(\u0026quot;progress\u0026quot;) @Default(\u0026quot;true\u0026quot;) boolean progress,\n URI[] sources,\n URI dest) {\n\n // TODO write the implementation...\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epackage\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eexample\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etoolz\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eCommand\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefault\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eOption\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eio\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eFile\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003enet\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eURI\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eregex\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ePattern\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eAnyName\u003c/span\u003e {\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ersync\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"recursive\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003erecursive\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"links\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003elinks\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"perms\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eperms\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"owner\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eowner\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"group\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003egroup\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"devices\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003edevices\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"specials\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003especials\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"times\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etimes\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"exclude\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eexclude\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"exclude-from\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eFile\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eexcludeFrom\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"include\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003einclude\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"include-from\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eFile\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eincludeFrom\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"progress\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"true\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eprogress\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003esources\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003edest\u003c/span\u003e) {\n\n \u003cspan class=\"pl-c\"\u003e// TODO write the implementation...\u003c/span\u003e\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eSome quick notes on \u003ccode\u003e@Command\u003c/code\u003e usage:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eMultiple classes that use \u003ccode\u003e@Command\u003c/code\u003e are allowed\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eMuttiple \u003ccode\u003e@Command\u003c/code\u003e methods are allowed in a class\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003e@Command\u003c/code\u003e methods in a class may have the same or different name\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eThe command name is derived from the method name if not specified in \u003ccode\u003e@Command\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 id=\"user-content-executing-the-command\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eExecuting the Command\u003c/h3\u003e\u003ca id=\"user-content-executing-the-command\" class=\"anchor\" aria-label=\"Permalink: Executing the Command\" href=\"#executing-the-command\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003ePack this class in an uber jar with the Crest library and you could execute this command from the command line as follows:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003e$ java -jar target/toolz-1.0.0-SNAPSHOT.jar rsync\nMissing argument: URI...\n\nUsage: rsync [options] URI... URI\n\nOptions:\n --devices\n --exclude=\u0026lt;Pattern\u0026gt;\n --exclude-from=\u0026lt;File\u0026gt;\n --group\n --include=\u0026lt;Pattern\u0026gt;\n --include-from=\u0026lt;File\u0026gt;\n --links\n --owner\n --perms\n --no-progress\n --recursive\n --specials\n --times\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eOf course, if we execute the command without the required arguments it will error out. This is the value of Crest — it does this dance for you.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIn a dozen and more years of writing tools on different teams, two truths seem to prevail:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e90% of writing scripts is parsing and validating user input\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eDon’t do that well and you’ll be lucky if it gets more than six months of use\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eComputers are easy, humans are complex. Let Crest deal with the humans, you just write code.\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-help-text\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eHelp Text\u003c/h2\u003e\u003ca id=\"user-content-help-text\" class=\"anchor\" aria-label=\"Permalink: Help Text\" href=\"#help-text\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIn the above example we have no details in our help other than what can be generated from inspecting the code. To add actual descriptions to our\ncode we simply need to put an \u003ccode\u003eOptionDescriptions.properties\u003c/code\u003e in the same package as our class.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003e#code\n# \u0026lt;option\u0026gt; = \u0026lt;description\u0026gt;\n# \u0026lt;command\u0026gt;.\u0026lt;option\u0026gt; = \u0026lt;description\u0026gt;\n# The most specific key always wins\n\nrecursive = recurse into directories\nlinks = copy symlinks as symlinks\nperms = preserve permissions\nowner = preserve owner (super-user only)\ngroup = preserve group\ntimes = preserve times\ndevices = preserve device files (super-user only)\nspecials = preserve special files\nexclude = exclude files matching PATTERN\nexclude-from = read exclude patterns from FILE\ninclude = don't exclude files matching PATTERN\ninclude-from = read include patterns from FILE\nprogress = this is not the description that will be chosen\nrsync.progress = don't show progress during transfer\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eSome quick notes on \u003ccode\u003eOptionDescription.properties\u003c/code\u003e files:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eThese are Java \u003ccode\u003ejava.util.ResourceBundle\u003c/code\u003e objects, so i18n is supported\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eUse \u003ccode\u003eOptionDescription_en.properties\u003c/code\u003e and similar for Locale specific help text\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eIn DRY spirit, every \u003ccode\u003e@Command\u003c/code\u003e in the package shares the same \u003ccode\u003eOptionDescription\u003c/code\u003e ResourceBundle and keys\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eUse \u003ccode\u003e\u0026lt;command\u0026gt;.\u0026lt;option\u0026gt;\u003c/code\u003e as the key for situations where sharing is not desired\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eWith the above in our classpath, our command’s help will now look like the following:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003e$ java -jar target/toolz-1.0.0-SNAPSHOT.jar rsync\nMissing argument: URI...\n\nUsage: rsync [options] URI... URI\n\nOptions:\n --devices preserve device files (super-user only)\n --exclude=\u0026lt;Pattern\u0026gt; exclude files matching PATTERN\n --exclude-from=\u0026lt;File\u0026gt; read exclude patterns from FILE\n --group preserve group\n --include=\u0026lt;Pattern\u0026gt; don't exclude files matching PATTERN\n --include-from=\u0026lt;File\u0026gt; read include patterns from FILE\n --links copy symlinks as symlinks\n --owner preserve owner (super-user only)\n --perms preserve permissions\n --no-progress don't show progress during transfer\n --recursive recurse into directories\n --specials preserve special files\n --times preserve times\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-bash-completion\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eBash Completion\u003c/h2\u003e\u003ca id=\"user-content-bash-completion\" class=\"anchor\" aria-label=\"Permalink: Bash Completion\" href=\"#bash-completion\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eFor those that use Bash as their shell, command completion can be added to your environment as follows:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003esource $(yourcommand _completion -f)\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eUsing the example \"toolz\" cli created above, this could work like so:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003esource $(java -jar target/toolz-1.0.0-SNAPSHOT.jar _completion -f)\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf the toolz-1.0.0-SNAPSHOT.jar was turned into an executable file called \u003ccode\u003etoolz\u003c/code\u003e via the \u003ccode\u003ereally-executable-jar\u003c/code\u003e maven plugin and \u003ccode\u003etoolz\u003c/code\u003e is in the system \u003ccode\u003ePATH\u003c/code\u003e, this would also work:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003esource $(toolz _completion -f)\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-default-values\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e@Default values\u003c/h2\u003e\u003ca id=\"user-content-default-values\" class=\"anchor\" aria-label=\"Permalink: @Default values\" href=\"#default-values\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eSetting defaults to the \u003ccode\u003e@Option\u003c/code\u003e parameters of our \u003ccode\u003e@Command\u003c/code\u003e method can be done via the \u003ccode\u003e@Default\u003c/code\u003e annotation. Using as\n simplified version of our \u003ccode\u003ersync\u003c/code\u003e\n example, we might possibly wish to specify a default \u003ccode\u003eexclude\u003c/code\u003e pattern.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic void rsync(@Option(\u0026quot;exclude\u0026quot;) @Default(\u0026quot;.*~\u0026quot;) Pattern exclude,\n @Option(\u0026quot;include\u0026quot;) Pattern include,\n @Option(\u0026quot;progress\u0026quot;) @Default(\u0026quot;true\u0026quot;) boolean progress,\n URI[] sources,\n URI dest) {\n\n // TODO write the implementation...\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ersync\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"exclude\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\".*~\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eexclude\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"include\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003einclude\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"progress\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"true\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eprogress\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003esources\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003edest\u003c/span\u003e) {\n\n \u003cspan class=\"pl-c\"\u003e// TODO write the implementation...\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eSome quick notes about \u003ccode\u003e@Option\u003c/code\u003e:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003e@Option\u003c/code\u003e parameters are, by default, optional\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eWhen \u003ccode\u003e@Default\u003c/code\u003e is not used, the value will be its equivalent JVM default — typically \u003ccode\u003e0\u003c/code\u003e or \u003ccode\u003enull\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eAdd \u003ccode\u003e@Required\u003c/code\u003e to force a user to specify a value\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eDefault values will show up in help output automatically, no need to update your \u003ccode\u003eOptionDescriptions.properties\u003c/code\u003e\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003eUsage: rsync [options] URI... URI\n\nOptions:\n --exclude=\u0026lt;Pattern\u0026gt; exclude files matching PATTERN\n (default: .*~)\n --include=\u0026lt;Pattern\u0026gt; don't exclude files matching PATTERN\n --no-progress don't show progress during transfer\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 id=\"user-content-advanced\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eAdvanced\u003c/h3\u003e\u003ca id=\"user-content-advanced\" class=\"anchor\" aria-label=\"Permalink: Advanced\" href=\"#advanced\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eDefault values also support interpolations:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic void myCommand(@Option(\u0026quot;myoption\u0026quot;) @Default(\u0026quot;${env.MY_ENV_VAR}\u0026quot;) String exclude) {\n // TODO write the implementation...\n}\n@Command\npublic void myCommand(@Option(\u0026quot;myoption\u0026quot;) @Default(\u0026quot;${sys.MY_ENV_VAR}\u0026quot;) String exclude) {\n // TODO write the implementation...\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003emyCommand\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"myoption\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"${env.MY_ENV_VAR}\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eexclude\u003c/span\u003e) {\n \u003cspan class=\"pl-c\"\u003e// TODO write the implementation...\u003c/span\u003e\n}\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003emyCommand\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"myoption\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"${sys.MY_ENV_VAR}\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eexclude\u003c/span\u003e) {\n \u003cspan class=\"pl-c\"\u003e// TODO write the implementation...\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eenv\u003c/code\u003e is a prefix used to read the default in the environment variables and \u003ccode\u003esys\u003c/code\u003e to read the system properties.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eTip\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\nyou can also register custom \u003ccode\u003eDefaultsContext\u003c/code\u003e in the interpolation registry using \u003ccode\u003eMETA-INF/services/org.tomitribe.crest.contexts.DefaultsContext\u003c/code\u003e\nfile to register it (just put a fully qualified implementation per line). The prefix will be the simple name of the implementation in lowercase. For instance\n\u003ccode\u003eorg.company.MyEnv\u003c/code\u003e will use \u003ccode\u003emyenv\u003c/code\u003e.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eFinally the interpolation in such a form supports defaults:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic void myCommand(@Option(\u0026quot;myoption\u0026quot;) @Default(\u0026quot;${env.MY_ENV_VAR:defaultIfEnvNotSet}\u0026quot;) String exclude) {\n // TODO write the implementation...\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003emyCommand\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"myoption\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"${env.MY_ENV_VAR:defaultIfEnvNotSet}\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eexclude\u003c/span\u003e) {\n \u003cspan class=\"pl-c\"\u003e// TODO write the implementation...\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-option-lists-and-arrays\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e@Option Lists and Arrays\u003c/h2\u003e\u003ca id=\"user-content-option-lists-and-arrays\" class=\"anchor\" aria-label=\"Permalink: @Option Lists and Arrays\" href=\"#option-lists-and-arrays\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThere are situations where you might want to allow the same flag to be specified twice. Simply turn the \u003ccode\u003e@Option\u003c/code\u003e parameter into an\narray or list that uses generics.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic void rsync(@Option(\u0026quot;exclude\u0026quot;) @Default(\u0026quot;.*~\u0026quot;) Pattern[] excludes,\n @Option(\u0026quot;include\u0026quot;) Pattern include,\n @Option(\u0026quot;progress\u0026quot;) @Default(\u0026quot;true\u0026quot;) boolean progress,\n URI[] sources,\n URI dest) {\n\n // TODO write the implementation...\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ersync\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"exclude\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\".*~\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003eexcludes\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"include\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003einclude\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"progress\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"true\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eprogress\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003esources\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003edest\u003c/span\u003e) {\n\n \u003cspan class=\"pl-c\"\u003e// TODO write the implementation...\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe user can now specify multiple values when invoking the command by repeating the flag.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"$ java -jar target/toolz-1.0.0-SNAPSHOT.jar rsync --exclude=\u0026quot;.*\\.log\u0026quot; --exclude=\u0026quot;.*\\.iml\u0026quot; ...\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003e$ java -jar target/toolz-1.0.0-SNAPSHOT.jar rsync --exclude=\".*\\.log\" --exclude=\".*\\.iml\" ...\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-default-option-lists-and-arrays\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e@Default @Option Lists and Arrays\u003c/h2\u003e\u003ca id=\"user-content-default-option-lists-and-arrays\" class=\"anchor\" aria-label=\"Permalink: @Default @Option Lists and Arrays\" href=\"#default-option-lists-and-arrays\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eShould you want to specify these two \u003ccode\u003eexclude\u003c/code\u003e values as the defaults, simply use a \u003cstrong\u003ecomma\u003c/strong\u003e \u003ccode\u003e,\u003c/code\u003e to separate them in \u003ccode\u003e@Default\u003c/code\u003e\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic void rsync(@Option(\u0026quot;exclude\u0026quot;) @Default(\u0026quot;.*\\\\.iml,.*\\\\.iml\u0026quot;) Pattern[] excludes,\n @Option(\u0026quot;include\u0026quot;) Pattern include,\n @Option(\u0026quot;progress\u0026quot;) @Default(\u0026quot;true\u0026quot;) boolean progress,\n URI[] sources,\n URI dest) {\n\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ersync\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"exclude\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\".*\u003cspan class=\"pl-cce\"\u003e\\\\\u003c/span\u003e.iml,.*\u003cspan class=\"pl-cce\"\u003e\\\\\u003c/span\u003e.iml\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003eexcludes\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"include\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003einclude\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"progress\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"true\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eprogress\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003esources\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003edest\u003c/span\u003e) {\n\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf you happen to need comma for something, use \u003cstrong\u003etab\u003c/strong\u003e \u003ccode\u003e\\t\u003c/code\u003e instead. When a tab is present in the \u003ccode\u003e@Default\u003c/code\u003e string, it becomes the preferred splitter.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic void rsync(@Option(\u0026quot;exclude\u0026quot;) @Default(\u0026quot;.*\\\\.iml\\t.*\\\\.iml\u0026quot;) Pattern[] excludes,\n @Option(\u0026quot;include\u0026quot;) Pattern include,\n @Option(\u0026quot;progress\u0026quot;) @Default(\u0026quot;true\u0026quot;) boolean progress,\n URI[] sources,\n URI dest) {\n\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ersync\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"exclude\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\".*\u003cspan class=\"pl-cce\"\u003e\\\\\u003c/span\u003e.iml\u003cspan class=\"pl-cce\"\u003e\\t\u003c/span\u003e.*\u003cspan class=\"pl-cce\"\u003e\\\\\u003c/span\u003e.iml\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003eexcludes\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"include\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003einclude\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"progress\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"true\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eprogress\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003esources\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003edest\u003c/span\u003e) {\n\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf you happen to need both tab and comma for something (really????), use \u003cstrong\u003eunicode\u003c/strong\u003e zero \u003ccode\u003e\\u0000\u003c/code\u003e instead.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic void rsync(@Option(\u0026quot;exclude\u0026quot;) @Default(\u0026quot;.*\\\\.iml\\u0000.*\\\\.iml\u0026quot;) Pattern[] excludes,\n @Option(\u0026quot;include\u0026quot;) Pattern include,\n @Option(\u0026quot;progress\u0026quot;) @Default(\u0026quot;true\u0026quot;) boolean progress,\n URI[] sources,\n URI dest) {\n\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ersync\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"exclude\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\".*\u003cspan class=\"pl-cce\"\u003e\\\\\u003c/span\u003e.iml\u003cspan class=\"pl-cce\"\u003e\\u0000\u003c/span\u003e.*\u003cspan class=\"pl-cce\"\u003e\\\\\u003c/span\u003e.iml\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003eexcludes\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"include\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003ePattern\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003einclude\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"progress\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"true\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eprogress\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003esources\u003c/span\u003e,\n \u003cspan class=\"pl-smi\"\u003eURI\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003edest\u003c/span\u003e) {\n\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-default-and-variable-substitution\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e@Default and ${variable} Substitution\u003c/h2\u003e\u003ca id=\"user-content-default-and-variable-substitution\" class=\"anchor\" aria-label=\"Permalink: @Default and ${variable} Substitution\" href=\"#default-and-variable-substitution\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIn the event you want to make defaults contextual, you can use \u003ccode\u003e${some.property}\u003c/code\u003e in the \u003ccode\u003e@Default\u003c/code\u003e string and\n the \u003ccode\u003ejava.lang.System.getProperties()\u003c/code\u003e object to supply the value.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic void hello(@Option(\u0026quot;name\u0026quot;) @Default(\u0026quot;${user.name}\u0026quot;) String user) throws Exception\n System.out.printf(\u0026quot;Hello, %s%n\u0026quot;, user);\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ehello\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"name\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"${user.name}\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003euser\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003ethrows\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eException\u003c/span\u003e\n \u003cspan class=\"pl-smi\"\u003eSystem\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eout\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eprintf\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"Hello, %s%n\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003euser\u003c/span\u003e);\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-return-values\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eReturn Values\u003c/h2\u003e\u003ca id=\"user-content-return-values\" class=\"anchor\" aria-label=\"Permalink: Return Values\" href=\"#return-values\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIn the above we wrote to the console, which is fine for simple things but can make testing hard. So far our commands are still POJOs and\nnothing is stopping us from unit testing them as plain java objects — except asserting output writen to \u003ccode\u003eSystem.out\u003c/code\u003e.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eSimply return \u003ccode\u003ejava.lang.String\u003c/code\u003e and it will be written to \u003ccode\u003eSystem.out\u003c/code\u003e for you.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic String hello(@Option(\u0026quot;name\u0026quot;) @Default(\u0026quot;${user.name}\u0026quot;) String user) throws Exception\n return String.format(\u0026quot;Hello, %s%n\u0026quot;, user);\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ehello\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"name\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"${user.name}\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003euser\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003ethrows\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eException\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eformat\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"Hello, %s%n\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003euser\u003c/span\u003e);\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIn the event you need to write a significant amount of data, you can return \u003ccode\u003eorg.tomitribe.crest.api.StreamingOutput\u003c/code\u003e which is an exact copy of the\nequivalent JAX-RS \u003ca href=\"http://docs.oracle.com/javaee/6/api/javax/ws/rs/core/StreamingOutput.html\" rel=\"nofollow\"\u003eStreamingOutput\u003c/a\u003e interface.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic StreamingOutput cat(final File file) {\n if (!file.exists()) throw new IllegalStateException(\u0026quot;File does not exist: \u0026quot; + file.getAbsolutePath());\n if (!file.canRead()) throw new IllegalStateException(\u0026quot;Not readable: \u0026quot; + file.getAbsolutePath());\n if (!file.isFile()) throw new IllegalStateException(\u0026quot;Not a file: \u0026quot; + file.getAbsolutePath());\n\n return new StreamingOutput() {\n @Override\n public void write(OutputStream output) throws IOException {\n final InputStream input = new BufferedInputStream(new FileInputStream(file));\n try {\n final byte[] buffer = new byte[1024];\n int length;\n while ((length = input.read(buffer)) != -1) {\n output.write(buffer, 0, length);\n }\n output.flush();\n } finally {\n if (input != null) input.close();\n }\n }\n };\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eStreamingOutput\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ecat\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eFile\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e (!\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eexists\u003c/span\u003e()) \u003cspan class=\"pl-k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIllegalStateException\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"File does not exist: \"\u003c/span\u003e + \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetAbsolutePath\u003c/span\u003e());\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e (!\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003ecanRead\u003c/span\u003e()) \u003cspan class=\"pl-k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIllegalStateException\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"Not readable: \"\u003c/span\u003e + \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetAbsolutePath\u003c/span\u003e());\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e (!\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eisFile\u003c/span\u003e()) \u003cspan class=\"pl-k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIllegalStateException\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"Not a file: \"\u003c/span\u003e + \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetAbsolutePath\u003c/span\u003e());\n\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eStreamingOutput\u003c/span\u003e() {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ewrite\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eOutputStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eoutput\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003ethrows\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIOException\u003c/span\u003e {\n \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eInputStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003einput\u003c/span\u003e = \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eBufferedInputStream\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eFileInputStream\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e));\n \u003cspan class=\"pl-k\"\u003etry\u003c/span\u003e {\n \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ebyte\u003c/span\u003e[] \u003cspan class=\"pl-s1\"\u003ebuffer\u003c/span\u003e = \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ebyte\u003c/span\u003e[\u003cspan class=\"pl-c1\"\u003e1024\u003c/span\u003e];\n \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003elength\u003c/span\u003e;\n \u003cspan class=\"pl-k\"\u003ewhile\u003c/span\u003e ((\u003cspan class=\"pl-s1\"\u003elength\u003c/span\u003e = \u003cspan class=\"pl-s1\"\u003einput\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eread\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ebuffer\u003c/span\u003e)) != -\u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e) {\n \u003cspan class=\"pl-s1\"\u003eoutput\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003ewrite\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ebuffer\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003elength\u003c/span\u003e);\n }\n \u003cspan class=\"pl-s1\"\u003eoutput\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eflush\u003c/span\u003e();\n } \u003cspan class=\"pl-k\"\u003efinally\u003c/span\u003e {\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e (\u003cspan class=\"pl-s1\"\u003einput\u003c/span\u003e != \u003cspan class=\"pl-c1\"\u003enull\u003c/span\u003e) \u003cspan class=\"pl-s1\"\u003einput\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eclose\u003c/span\u003e();\n }\n }\n };\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eNote a \u003ccode\u003enull\u003c/code\u003e check is not necessary for the \u003ccode\u003eFile file\u003c/code\u003e parameter as Crest will not let the value of any plain argument be unspecified. All parameters which do not use \u003ccode\u003e@Option\u003c/code\u003e are treated as required\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-stream-injections\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eStream injections\u003c/h2\u003e\u003ca id=\"user-content-stream-injections\" class=\"anchor\" aria-label=\"Permalink: Stream injections\" href=\"#stream-injections\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eCommand are often linked to console I/O. For that reason it is important to be able to interact\nwith Crest in/out/error streams. They are provided by the contextual \u003ccode\u003eEnvironment\u003c/code\u003e instance and using its thread local\nyou can retrieve them. However to make it easier to work with you can inject them as well.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eOut stream (out and error ones) needs to be \u003ccode\u003ePrintStream\u003c/code\u003e typed and input is typed as a \u003ccode\u003eInputStream\u003c/code\u003e.\nJust use these types as command parameters and decorate it with \u003ccode\u003e@In\u003c/code\u003e/\u003ccode\u003e@Out\u003c/code\u003e/\u003ccode\u003e@Err\u003c/code\u003e:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public class IOMe {\n @org.tomitribe.crest.api.Command\n public static void asserts(@In final InputStream in,\n @Out final PrintStream out,\n @Err PrintStream err) {\n // ...\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIOMe\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eCommand\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003easserts\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eIn\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eInputStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ein\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOut\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ePrintStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eout\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eErr\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ePrintStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e) {\n \u003cspan class=\"pl-c\"\u003e// ...\u003c/span\u003e\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eNote\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\nusing a parameter typed \u003ccode\u003eEnvironment\u003c/code\u003e you’ll get it injected as well but this one is not in \u003ccode\u003ecrest-api\u003c/code\u003e.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-custom-java-types\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eCustom Java Types\u003c/h2\u003e\u003ca id=\"user-content-custom-java-types\" class=\"anchor\" aria-label=\"Permalink: Custom Java Types\" href=\"#custom-java-types\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eYou may have been seeing \u003ccode\u003eFile\u003c/code\u003e and \u003ccode\u003ePattern\u003c/code\u003e in the above examples and wondering exactly which Java classes Crest supports parameters to \u003ccode\u003e@Command\u003c/code\u003e methods.\nThe short answer is, any. Crest does \u003cstrong\u003enot\u003c/strong\u003e use \u003ccode\u003ejava.beans.PropertyEditor\u003c/code\u003e implementations by default like libraries such as Spring do.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eAfter nearly 20 years of Java’s existence, it’s safe to say two styles dominate converting a \u003ccode\u003eString\u003c/code\u003e into a Java object:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eA \u003cstrong\u003eConstructor\u003c/strong\u003e that take a single String as an argument. Examples:\u003c/p\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003ejava.io.File(String)\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003ejava.lang.Integer(String)\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003ejava.net.URL(String)\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eA \u003cstrong\u003estatic method\u003c/strong\u003e that returns an instance of the same class. Examples:\u003c/p\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003ejava.util.regex.Pattern.compile(String)\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003ejava.net.URI.create(String)\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003ejava.util.concurrent.TimeUnit.valueOf(String)\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eUse either of these conventions and Crest will have no problem instantiating your object with the user-supplied \u003ccode\u003eString\u003c/code\u003e from the command-line args.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThis should cover \u003cstrong\u003e95%\u003c/strong\u003e of all cases, but in the event it does not, you can create a \u003ccode\u003ejava.beans.PropertyEditor\u003c/code\u003e and register it with the JVM.\nUse your Google-fu to learn how to do that.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe order of precedence is as follows:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eConstructor\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eStatic method\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003ejava.beans.PropertyEditor\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-custom-validation\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eCustom Validation\u003c/h2\u003e\u003ca id=\"user-content-custom-validation\" class=\"anchor\" aria-label=\"Permalink: Custom Validation\" href=\"#custom-validation\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf we look at our \u003ccode\u003ecat\u003c/code\u003e command we had earlier and yank the very boiler-plate read/write stream logic, all we have left is some code validating the user input.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic StreamingOutput cat(final File file) {\n if (!file.exists()) throw new IllegalStateException(\u0026quot;File does not exist: \u0026quot; + file.getAbsolutePath());\n if (!file.canRead()) throw new IllegalStateException(\u0026quot;Not readable: \u0026quot; + file.getAbsolutePath());\n if (!file.isFile()) throw new IllegalStateException(\u0026quot;Not a file: \u0026quot; + file.getAbsolutePath());\n\n return new StreamingOutput() {\n @Override\n public void write(OutputStream os) throws IOException {\n IO.copy(file, os);\n }\n };\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eStreamingOutput\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ecat\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eFile\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e (!\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eexists\u003c/span\u003e()) \u003cspan class=\"pl-k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIllegalStateException\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"File does not exist: \"\u003c/span\u003e + \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetAbsolutePath\u003c/span\u003e());\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e (!\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003ecanRead\u003c/span\u003e()) \u003cspan class=\"pl-k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIllegalStateException\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"Not readable: \"\u003c/span\u003e + \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetAbsolutePath\u003c/span\u003e());\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e (!\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eisFile\u003c/span\u003e()) \u003cspan class=\"pl-k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIllegalStateException\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"Not a file: \"\u003c/span\u003e + \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetAbsolutePath\u003c/span\u003e());\n\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eStreamingOutput\u003c/span\u003e() {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ewrite\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eOutputStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eos\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003ethrows\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIOException\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003eIO\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003ecopy\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eos\u003c/span\u003e);\n }\n };\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThis validation code, too, can be yanked. Crest supports the use of \u003ca href=\"http://beanvalidation.org\" rel=\"nofollow\"\u003eBean Validation\u003c/a\u003e to validate \u003ccode\u003e@Command\u003c/code\u003e method\nparameters.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic StreamingOutput cat(@Exists @Readable final File file) {\n if (!file.isFile()) throw new IllegalStateException(\u0026quot;Not a file: \u0026quot; + file.getAbsolutePath());\n\n return new StreamingOutput() {\n @Override\n public void write(OutputStream os) throws IOException {\n IO.copy(file, os);\n }\n };\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eStreamingOutput\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ecat\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eExists\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eReadable\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eFile\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e (!\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eisFile\u003c/span\u003e()) \u003cspan class=\"pl-k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIllegalStateException\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"Not a file: \"\u003c/span\u003e + \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetAbsolutePath\u003c/span\u003e());\n\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eStreamingOutput\u003c/span\u003e() {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ewrite\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eOutputStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eos\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003ethrows\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIOException\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003eIO\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003ecopy\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eos\u003c/span\u003e);\n }\n };\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eHere we’ve eliminated two of our very tedious checks with Bean Validation annotations that Crest provides out of the box, but we still have one more to\nget rid of. We can eliminate that one by writing our own annotation and using the Bean Validation API to wire it all together.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eHere is what an annotation to do the \u003ccode\u003efile.isFile()\u003c/code\u003e check might look like — let’s call the annotation simply \u003ccode\u003e@IsFile\u003c/code\u003e\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"package org.example.toolz;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport javax.validation.Payload;\nimport java.io.File;\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport org.tomitribe.crest.val.Exists;\n\nimport static java.lang.annotation.ElementType.ANNOTATION_TYPE;\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.ElementType.METHOD;\nimport static java.lang.annotation.ElementType.PARAMETER;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\n@Exists\n@Documented\n@javax.validation.Constraint(validatedBy = {IsFile.Constraint.class})\n@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER})\n@Retention(RUNTIME)\npublic @interface IsFile {\n Class\u0026lt;?\u0026gt;[] groups() default {};\n\n String message() default \u0026quot;{org.exampe.toolz.IsFile.message}\u0026quot;;\n\n Class\u0026lt;? extends Payload\u0026gt;[] payload() default {};\n\n public static class Constraint implements ConstraintValidator\u0026lt;IsFile, File\u0026gt; {\n\n @Override\n public void initialize(IsFile constraintAnnotation) {\n }\n\n @Override\n public boolean isValid(File file, ConstraintValidatorContext context) {\n return file.isFile();\n }\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epackage\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eexample\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etoolz\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejavax\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003evalidation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eConstraintValidator\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejavax\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003evalidation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eConstraintValidatorContext\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejavax\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003evalidation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ePayload\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eio\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eFile\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003elang\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eannotation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDocumented\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003elang\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eannotation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eRetention\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003elang\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eannotation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eTarget\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eval\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eExists\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003elang\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eannotation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eElementType\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eANNOTATION_TYPE\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003elang\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eannotation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eElementType\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eFIELD\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003elang\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eannotation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eElementType\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eMETHOD\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003elang\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eannotation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eElementType\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003ePARAMETER\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003elang\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eannotation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eRetentionPolicy\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRUNTIME\u003c/span\u003e;\n\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eExists\u003c/span\u003e\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDocumented\u003c/span\u003e\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003ejavax\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003evalidation\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eConstraint\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003evalidatedBy\u003c/span\u003e = {\u003cspan class=\"pl-smi\"\u003eIsFile\u003c/span\u003e.\u003cspan class=\"pl-smi\"\u003eConstraint\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e})\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eTarget\u003c/span\u003e({\u003cspan class=\"pl-c1\"\u003eMETHOD\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003eFIELD\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003eANNOTATION_TYPE\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003ePARAMETER\u003c/span\u003e})\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eRetention\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003eRUNTIME\u003c/span\u003e)\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e @interface \u003cspan class=\"pl-s1\"\u003eIsFile\u003c/span\u003e {\n \u003cspan class=\"pl-smi\"\u003eClass\u003c/span\u003e\u0026lt;?\u0026gt;[] \u003cspan class=\"pl-s1\"\u003egroups\u003c/span\u003e() \u003cspan class=\"pl-k\"\u003edefault\u003c/span\u003e {};\n\n \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003emessage\u003c/span\u003e() \u003cspan class=\"pl-k\"\u003edefault\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\"{org.exampe.toolz.IsFile.message}\"\u003c/span\u003e;\n\n \u003cspan class=\"pl-smi\"\u003eClass\u003c/span\u003e\u0026lt;? \u003cspan class=\"pl-k\"\u003eextends\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ePayload\u003c/span\u003e\u0026gt;[] \u003cspan class=\"pl-s1\"\u003epayload\u003c/span\u003e() \u003cspan class=\"pl-k\"\u003edefault\u003c/span\u003e {};\n\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eConstraint\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eimplements\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eConstraintValidator\u003c/span\u003e\u0026lt;\u003cspan class=\"pl-smi\"\u003eIsFile\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eFile\u003c/span\u003e\u0026gt; {\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003einitialize\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eIsFile\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003econstraintAnnotation\u003c/span\u003e) {\n }\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eisValid\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eFile\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eConstraintValidatorContext\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003econtext\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eisFile\u003c/span\u003e();\n }\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eWe can then update our code as follows to use this validation and eliminate all our boiler-plate.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic StreamingOutput cat(@IsFile @Readable final File file) {\n\n return new StreamingOutput() {\n @Override\n public void write(OutputStream os) throws IOException {\n IO.copy(file, os);\n }\n };\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eStreamingOutput\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ecat\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eIsFile\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eReadable\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eFile\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e) {\n\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eStreamingOutput\u003c/span\u003e() {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ewrite\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eOutputStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eos\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003ethrows\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIOException\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003eIO\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003ecopy\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eos\u003c/span\u003e);\n }\n };\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eNotice that we also removed \u003ccode\u003e@Exists\u003c/code\u003e from the method parameter? Since we put \u003ccode\u003e@Exists\u003c/code\u003e on the \u003ccode\u003e@IsFile\u003c/code\u003e annotation,\nthe \u003ccode\u003e@IsFile\u003c/code\u003e annotation effectively inherits the \u003ccode\u003e@Exists\u003c/code\u003e logic.\nOur \u003ccode\u003e@IsFile\u003c/code\u003e annotation could inherit any number of annotations this way.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eAs the true strength of a great library of tools is the effort put into ensuring correct input, it’s very wise to\nbite the bullet and proactively invest in creating a reusable set of validation annotations to cover your typical input\ntypes.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003ePull requests are \u003cstrong\u003every\u003c/strong\u003e strongly encouraged for any annotations that might be useful to others.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 id=\"user-content-bean-validation-less-validations\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eBean Validation-less validations\u003c/h3\u003e\u003ca id=\"user-content-bean-validation-less-validations\" class=\"anchor\" aria-label=\"Permalink: Bean Validation-less validations\" href=\"#bean-validation-less-validations\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eYou can also use the built-in crest validator style, it enables to lighten the dependencies by not requiring bean validation.\nTo do that you must:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003edefine a custom validation annotation\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eimplementation the validation as a \u003ccode\u003eConsumer\u0026lt;ParamType\u0026gt;\u003c/code\u003e or \u003ccode\u003eBiConsumer\u0026lt;AnnotationDefinedIn1, ParamType\u0026gt;\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf the validation fails, the implementation just throws an exception with a meaningful error message.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eHere is a trivial example to check a \u003ccode\u003ePath\u003c/code\u003e is a directory:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\" @Target(PARAMETER)\n @Retention(RUNTIME)\n @Validation(CrestDirectory.Impl.class)\n public @interface CrestDirectory {\n }\n\n public class Impl implements Consumer\u0026lt;Path\u0026gt; {\n @Override\n public void accept(final Path file) {\n if (!Files.isDirectory(file)) {\n throw new IllegalStateException(\u0026quot;'\u0026quot; + file + \u0026quot;' is not a directory\u0026quot;);\n }\n }\n}\"\u003e\u003cpre\u003e \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eTarget\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003ePARAMETER\u003c/span\u003e)\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eRetention\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003eRUNTIME\u003c/span\u003e)\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eValidation\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eCrestDirectory\u003c/span\u003e.\u003cspan class=\"pl-smi\"\u003eImpl\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e @interface \u003cspan class=\"pl-s1\"\u003eCrestDirectory\u003c/span\u003e {\n }\n\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eImpl\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eimplements\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eConsumer\u003c/span\u003e\u0026lt;\u003cspan class=\"pl-smi\"\u003ePath\u003c/span\u003e\u0026gt; {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eaccept\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ePath\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e (!\u003cspan class=\"pl-smi\"\u003eFiles\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eisDirectory\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e)) {\n \u003cspan class=\"pl-k\"\u003ethrow\u003c/span\u003e \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIllegalStateException\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"'\"\u003c/span\u003e + \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e + \u003cspan class=\"pl-s\"\u003e\"' is not a directory\"\u003c/span\u003e);\n }\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eTip\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\nthe instances of the implementation are looked up by class in the \u003ccode\u003eEnvironment\u003c/code\u003e and if none matches a plain \u003ccode\u003enew\u003c/code\u003e is done calling the default constructor.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-maven-pom-xml-setup\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eMaven pom.xml setup\u003c/h2\u003e\u003ca id=\"user-content-maven-pomxml-setup\" class=\"anchor\" aria-label=\"Permalink: Maven pom.xml setup\" href=\"#maven-pomxml-setup\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe following sample pom.xml will get you 90% of your way to fun with Crest and project\nthat will output a small uber jar with all the required dependencies.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;?xml version=\u0026quot;1.0\u0026quot;?\u0026gt;\n\u0026lt;project xsi:schemaLocation=\u0026quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\u0026quot; xmlns=\u0026quot;http://maven.apache.org/POM/4.0.0\u0026quot;\n xmlns:xsi=\u0026quot;http://www.w3.org/2001/XMLSchema-instance\u0026quot;\u0026gt;\n \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\n \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;toolz\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;0.3-SNAPSHOT\u0026lt;/version\u0026gt;\n\n \u0026lt;dependencies\u0026gt;\n \u0026lt;dependency\u0026gt;\n \u0026lt;groupId\u0026gt;org.tomitribe\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;tomitribe-crest\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;0.3-SNAPSHOT\u0026lt;/version\u0026gt;\n \u0026lt;/dependency\u0026gt;\n \u0026lt;dependency\u0026gt;\n \u0026lt;groupId\u0026gt;junit\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;junit\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;4.10\u0026lt;/version\u0026gt;\n \u0026lt;scope\u0026gt;test\u0026lt;/scope\u0026gt;\n \u0026lt;/dependency\u0026gt;\n\n \u0026lt;!-- Add tomitribe-crest-xbean if you want classpath scanning for @Command --\u0026gt;\n \u0026lt;dependency\u0026gt;\n \u0026lt;groupId\u0026gt;org.tomitribe\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;tomitribe-crest-xbean\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;0.3-SNAPSHOT\u0026lt;/version\u0026gt;\n \u0026lt;/dependency\u0026gt;\n \u0026lt;/dependencies\u0026gt;\n\n \u0026lt;build\u0026gt;\n \u0026lt;defaultGoal\u0026gt;install\u0026lt;/defaultGoal\u0026gt;\n \u0026lt;plugins\u0026gt;\n \u0026lt;plugin\u0026gt;\n \u0026lt;artifactId\u0026gt;maven-shade-plugin\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;2.1\u0026lt;/version\u0026gt;\n \u0026lt;executions\u0026gt;\n \u0026lt;execution\u0026gt;\n \u0026lt;phase\u0026gt;package\u0026lt;/phase\u0026gt;\n \u0026lt;goals\u0026gt;\n \u0026lt;goal\u0026gt;shade\u0026lt;/goal\u0026gt;\n \u0026lt;/goals\u0026gt;\n \u0026lt;configuration\u0026gt;\n \u0026lt;transformers\u0026gt;\n \u0026lt;transformer implementation=\u0026quot;org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\u0026quot;\u0026gt;\n \u0026lt;mainClass\u0026gt;org.tomitribe.crest.Main\u0026lt;/mainClass\u0026gt;\n \u0026lt;/transformer\u0026gt;\n \u0026lt;/transformers\u0026gt;\n \u0026lt;/configuration\u0026gt;\n \u0026lt;/execution\u0026gt;\n \u0026lt;/executions\u0026gt;\n \u0026lt;/plugin\u0026gt;\n \u0026lt;/plugins\u0026gt;\n \u0026lt;/build\u0026gt;\n\n \u0026lt;repositories\u0026gt;\n \u0026lt;repository\u0026gt;\n \u0026lt;id\u0026gt;sonatype-nexus-snapshots\u0026lt;/id\u0026gt;\n \u0026lt;name\u0026gt;Sonatype Nexus Snapshots\u0026lt;/name\u0026gt;\n \u0026lt;url\u0026gt;https://oss.sonatype.org/content/repositories/snapshots\u0026lt;/url\u0026gt;\n \u0026lt;releases\u0026gt;\n \u0026lt;enabled\u0026gt;false\u0026lt;/enabled\u0026gt;\n \u0026lt;/releases\u0026gt;\n \u0026lt;snapshots\u0026gt;\n \u0026lt;enabled\u0026gt;true\u0026lt;/enabled\u0026gt;\n \u0026lt;/snapshots\u0026gt;\n \u0026lt;/repository\u0026gt;\n \u0026lt;/repositories\u0026gt;\n\n\u0026lt;/project\u0026gt;\"\u003e\u003cpre\u003e\u0026lt;?\u003cspan class=\"pl-ent\"\u003exml\u003c/span\u003e\u003cspan class=\"pl-e\"\u003e version\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e1.0\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e?\u0026gt;\n\u0026lt;\u003cspan class=\"pl-ent\"\u003eproject\u003c/span\u003e \u003cspan class=\"pl-e\"\u003exsi\u003c/span\u003e\u003cspan class=\"pl-e\"\u003e:\u003c/span\u003e\u003cspan class=\"pl-e\"\u003eschemaLocation\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ehttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e \u003cspan class=\"pl-e\"\u003exmlns\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ehttp://maven.apache.org/POM/4.0.0\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\n \u003cspan class=\"pl-e\"\u003exmlns\u003c/span\u003e\u003cspan class=\"pl-e\"\u003e:\u003c/span\u003e\u003cspan class=\"pl-e\"\u003exsi\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ehttp://www.w3.org/2001/XMLSchema-instance\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003emodelVersion\u003c/span\u003e\u0026gt;4.0.0\u0026lt;/\u003cspan class=\"pl-ent\"\u003emodelVersion\u003c/span\u003e\u0026gt;\n\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.example\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;toolz\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;0.3-SNAPSHOT\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.tomitribe\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;tomitribe-crest\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;0.3-SNAPSHOT\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;junit\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;junit\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;4.10\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003escope\u003c/span\u003e\u0026gt;test\u0026lt;/\u003cspan class=\"pl-ent\"\u003escope\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e Add tomitribe-crest-xbean if you want classpath scanning for @Command \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.tomitribe\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;tomitribe-crest-xbean\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;0.3-SNAPSHOT\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n\n \u0026lt;\u003cspan class=\"pl-ent\"\u003ebuild\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edefaultGoal\u003c/span\u003e\u0026gt;install\u0026lt;/\u003cspan class=\"pl-ent\"\u003edefaultGoal\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eplugins\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;maven-shade-plugin\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;2.1\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eexecutions\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eexecution\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003ephase\u003c/span\u003e\u0026gt;package\u0026lt;/\u003cspan class=\"pl-ent\"\u003ephase\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egoals\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egoal\u003c/span\u003e\u0026gt;shade\u0026lt;/\u003cspan class=\"pl-ent\"\u003egoal\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003egoals\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etransformers\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etransformer\u003c/span\u003e \u003cspan class=\"pl-e\"\u003eimplementation\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eorg.apache.maven.plugins.shade.resource.ManifestResourceTransformer\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003emainClass\u003c/span\u003e\u0026gt;org.tomitribe.crest.Main\u0026lt;/\u003cspan class=\"pl-ent\"\u003emainClass\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003etransformer\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003etransformers\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eexecution\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eexecutions\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugins\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003ebuild\u003c/span\u003e\u0026gt;\n\n \u0026lt;\u003cspan class=\"pl-ent\"\u003erepositories\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003erepository\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eid\u003c/span\u003e\u0026gt;sonatype-nexus-snapshots\u0026lt;/\u003cspan class=\"pl-ent\"\u003eid\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003ename\u003c/span\u003e\u0026gt;Sonatype Nexus Snapshots\u0026lt;/\u003cspan class=\"pl-ent\"\u003ename\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eurl\u003c/span\u003e\u0026gt;https://oss.sonatype.org/content/repositories/snapshots\u0026lt;/\u003cspan class=\"pl-ent\"\u003eurl\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003ereleases\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eenabled\u003c/span\u003e\u0026gt;false\u0026lt;/\u003cspan class=\"pl-ent\"\u003eenabled\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003ereleases\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003esnapshots\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eenabled\u003c/span\u003e\u0026gt;true\u0026lt;/\u003cspan class=\"pl-ent\"\u003eenabled\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003esnapshots\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003erepository\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003erepositories\u003c/span\u003e\u0026gt;\n\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003eproject\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-bean-parameter-binding\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eBean Parameter Binding\u003c/h2\u003e\u003ca id=\"user-content-bean-parameter-binding\" class=\"anchor\" aria-label=\"Permalink: Bean Parameter Binding\" href=\"#bean-parameter-binding\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf you don’t want to inject in all your commands the same N parameters you can modelize them as an object.\nJust use standard parameters as constructor parameters of the bean:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public class ColorfulCmd {\n @Command\n public static void exec(final Color color) {\n // ...\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColorfulCmd\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eexec\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecolor\u003c/span\u003e) {\n \u003cspan class=\"pl-c\"\u003e// ...\u003c/span\u003e\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTo identify \u003ccode\u003eColor\u003c/code\u003e as an \"option aware\" parameter just decorate it with \u003ccode\u003e@Options\u003c/code\u003e:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Options\npublic class Color { // getters omitted for brevity\n private final int r;\n private final int g;\n private final int b;\n private final int a;\n\n public Color(@Option(\u0026quot;r\u0026quot;) @Default(\u0026quot;255\u0026quot;) final int r,\n @Option(\u0026quot;g\u0026quot;) @Default(\u0026quot;255\u0026quot;) final int g,\n @Option(\u0026quot;b\u0026quot;) @Default(\u0026quot;255\u0026quot;) final int b,\n @Option(\u0026quot;a\u0026quot;) @Default(\u0026quot;255\u0026quot;) final int a) {\n this.r = r;\n this.g = g;\n this.b = b;\n this.a = a;\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOptions\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColor\u003c/span\u003e { \u003cspan class=\"pl-c\"\u003e// getters omitted for brevity\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003eprivate\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003er\u003c/span\u003e;\n \u003cspan class=\"pl-k\"\u003eprivate\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eg\u003c/span\u003e;\n \u003cspan class=\"pl-k\"\u003eprivate\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eb\u003c/span\u003e;\n \u003cspan class=\"pl-k\"\u003eprivate\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ea\u003c/span\u003e;\n\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColor\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"r\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"255\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003er\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"g\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"255\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eg\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"b\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"255\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eb\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"a\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"255\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ea\u003c/span\u003e) {\n \u003cspan class=\"pl-smi\"\u003ethis\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003er\u003c/span\u003e = \u003cspan class=\"pl-s1\"\u003er\u003c/span\u003e;\n \u003cspan class=\"pl-smi\"\u003ethis\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eg\u003c/span\u003e = \u003cspan class=\"pl-s1\"\u003eg\u003c/span\u003e;\n \u003cspan class=\"pl-smi\"\u003ethis\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eb\u003c/span\u003e = \u003cspan class=\"pl-s1\"\u003eb\u003c/span\u003e;\n \u003cspan class=\"pl-smi\"\u003ethis\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ea\u003c/span\u003e = \u003cspan class=\"pl-s1\"\u003ea\u003c/span\u003e;\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 id=\"user-content-prefixing-options\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003ePrefixing options\u003c/h3\u003e\u003ca id=\"user-content-prefixing-options\" class=\"anchor\" aria-label=\"Permalink: Prefixing options\" href=\"#prefixing-options\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf you reuse the same parameter N times you’ll probably want to prefix options. If we take previous example (\u003ccode\u003eParams\u003c/code\u003e)\nyou can desire to use \u003ccode\u003e--background.r\u003c/code\u003e and \u003ccode\u003e--foreground.r\u003c/code\u003e (same for g, b, a).\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eJust use \u003ccode\u003e@Option\u003c/code\u003e in the method parameter to do so:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public class ColorfulCmd {\n @Command\n public static void exec(@Option(\u0026quot;background.\u0026quot;) final Color colorBg, @Option(\u0026quot;foreground.\u0026quot;) final Color colorFg) {\n // ...\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColorfulCmd\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eexec\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"background.\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecolorBg\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"foreground.\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecolorFg\u003c/span\u003e) {\n \u003cspan class=\"pl-c\"\u003e// ...\u003c/span\u003e\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eNote\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\nthe '.' is not automatically added to allow you use to another convention like '-' or '_' ones for instance.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 id=\"user-content-override-defaults\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eOverride defaults\u003c/h3\u003e\u003ca id=\"user-content-override-defaults\" class=\"anchor\" aria-label=\"Permalink: Override defaults\" href=\"#override-defaults\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf you reuse the same parameter model accross command parameter you’ll surely want to override some default in some cases.\nFor that purpose just use \u003ccode\u003e@Defaults\u003c/code\u003e and define the mappings you want:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public class ColorfulCmd {\n @Command\n public static void exec(@Defaults({\n @Defaults.DefaultMapping(name = \u0026quot;r\u0026quot;, value = \u0026quot;0\u0026quot;),\n @Defaults.DefaultMapping(name = \u0026quot;g\u0026quot;, value = \u0026quot;0\u0026quot;),\n @Defaults.DefaultMapping(name = \u0026quot;b\u0026quot;, value = \u0026quot;0\u0026quot;),\n @Defaults.DefaultMapping(name = \u0026quot;a\u0026quot;, value = \u0026quot;0\u0026quot;)\n })\n @Option(\u0026quot;background.\u0026quot;)\n final Color colorBg,\n\n @Defaults({\n @Defaults.DefaultMapping(name = \u0026quot;r\u0026quot;, value = \u0026quot;255\u0026quot;),\n @Defaults.DefaultMapping(name = \u0026quot;g\u0026quot;, value = \u0026quot;255\u0026quot;),\n @Defaults.DefaultMapping(name = \u0026quot;b\u0026quot;, value = \u0026quot;255\u0026quot;),\n @Defaults.DefaultMapping(name = \u0026quot;a\u0026quot;, value = \u0026quot;255\u0026quot;)\n })\n @Option(\u0026quot;foreground.\u0026quot;)\n final Color colorFg) {\n // ...\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColorfulCmd\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eexec\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefaults\u003c/span\u003e({\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eDefaults\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefaultMapping\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"r\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"0\"\u003c/span\u003e),\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eDefaults\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefaultMapping\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"g\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"0\"\u003c/span\u003e),\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eDefaults\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefaultMapping\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"b\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"0\"\u003c/span\u003e),\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eDefaults\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefaultMapping\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"a\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"0\"\u003c/span\u003e)\n })\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"background.\"\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecolorBg\u003c/span\u003e,\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefaults\u003c/span\u003e({\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eDefaults\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefaultMapping\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"r\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"255\"\u003c/span\u003e),\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eDefaults\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefaultMapping\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"g\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"255\"\u003c/span\u003e),\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eDefaults\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefaultMapping\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"b\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"255\"\u003c/span\u003e),\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eDefaults\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefaultMapping\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"a\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"255\"\u003c/span\u003e)\n })\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"foreground.\"\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eColor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecolorFg\u003c/span\u003e) {\n \u003cspan class=\"pl-c\"\u003e// ...\u003c/span\u003e\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 id=\"user-content-interceptors\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eInterceptors\u003c/h3\u003e\u003ca id=\"user-content-interceptors\" class=\"anchor\" aria-label=\"Permalink: Interceptors\" href=\"#interceptors\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eSometimes you need to modify the command invocation or \"insert\" code before/after the command execution. For that purpose crest has some light\ninterceptor support.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eDefining an interceptor is as easy as defining a class with:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public static class MyInterceptor {\n @CrestInterceptor\n public Object intercept(final CrestContext crestContext) {\n return crestContext.proceed();\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eMyInterceptor\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCrestInterceptor\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eObject\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eintercept\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eCrestContext\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecrestContext\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecrestContext\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eproceed\u003c/span\u003e();\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe constraint for an interceptor are:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003ebeing decorated with \u003ccode\u003e@CrestInterceptor\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003ethe method needs to be public\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003ethe method needs to table a single parameter of type \u003ccode\u003eCrestContext\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eNote\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\nyou can pass \u003ccode\u003e@CrestInterceptor\u003c/code\u003e a value changing the key used to mark the interceptor.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTo let a command use an interceptor or multiple ones just list them ordered in \u003ccode\u003einterceptedBy\u003c/code\u003e parameter:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command(interceptedBy = { MySecurityInterceptor.class, MyLoggingInterceptor.class, MyParameterFillingInterceptor.class })\npublic void test1(\n @Option(\u0026quot;o1\u0026quot;) final String o1,\n @Option(\u0026quot;o2\u0026quot;) final int o2,\n @Err final PrintStream err,\n @Out final PrintStream out,\n @In final InputStream is,\n @Option(\u0026quot;o3\u0026quot;) final String o3,\n final URL url) {\n // do something\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003einterceptedBy\u003c/span\u003e = { \u003cspan class=\"pl-smi\"\u003eMySecurityInterceptor\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eMyLoggingInterceptor\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eMyParameterFillingInterceptor\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e })\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003etest1\u003c/span\u003e(\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"o1\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eo1\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"o2\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eo2\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eErr\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ePrintStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOut\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ePrintStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eout\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eIn\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eInputStream\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eis\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"o3\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eo3\u003c/span\u003e,\n \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eURL\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eurl\u003c/span\u003e) {\n \u003cspan class=\"pl-c\"\u003e// do something\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eCrest supports 3 styles of declaring interceptors\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 id=\"user-content-via-commandinterceptedby\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eVia \u003ccode\u003e@Command(interceptedBy)\u003c/code\u003e\u003c/h4\u003e\u003ca id=\"user-content-via-commandinterceptedby\" class=\"anchor\" aria-label=\"Permalink: Via @Command(interceptedBy)\" href=\"#via-commandinterceptedby\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003e@Command\u003c/code\u003e declaration uses the \u003ccode\u003einterceptedBy\u003c/code\u003e attribute to name the interceptor class.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public static class Foo {\n\n @Command(interceptedBy = GreenInterceptor.class)\n public String fighters(final String arg) {\n return arg;\n }\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eFoo\u003c/span\u003e {\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003einterceptedBy\u003c/span\u003e = \u003cspan class=\"pl-smi\"\u003eGreenInterceptor\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-en\"\u003efighters\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e;\n }\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003eGreenInterceptor\u003c/code\u003e definition is as usual\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public class GreenInterceptor {\n\n @CrestInterceptor\n public Object intercept(final CrestContext crestContext) {\n return crestContext.proceed();\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eGreenInterceptor\u003c/span\u003e {\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCrestInterceptor\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eObject\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eintercept\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eCrestContext\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecrestContext\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecrestContext\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eproceed\u003c/span\u003e();\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 id=\"user-content-custom-annotation-containing-crestinterceptorfoointerceptor-class\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eCustom annotation containing \u003ccode\u003e@CrestInterceptor(FooInterceptor.class)\u003c/code\u003e\u003c/h4\u003e\u003ca id=\"user-content-custom-annotation-containing-crestinterceptorfoointerceptorclass\" class=\"anchor\" aria-label=\"Permalink: Custom annotation containing @CrestInterceptor(FooInterceptor.class)\" href=\"#custom-annotation-containing-crestinterceptorfoointerceptorclass\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIn this style, we define our own custom annotation \u003ccode\u003e@Red\u003c/code\u003e that names \u003ccode\u003eRedInterceptor\u003c/code\u003e directly\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@CrestInterceptor(RedInterceptor.class)\n@Retention(value = RetentionPolicy.RUNTIME)\n@Target({ElementType.METHOD})\npublic @interface Red {\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCrestInterceptor\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eRedInterceptor\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e)\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eRetention\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-smi\"\u003eRetentionPolicy\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRUNTIME\u003c/span\u003e)\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eTarget\u003c/span\u003e({\u003cspan class=\"pl-smi\"\u003eElementType\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eMETHOD\u003c/span\u003e})\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e @interface \u003cspan class=\"pl-s1\"\u003eRed\u003c/span\u003e {\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003e…and use it on our \u003ccode\u003e@Command\u003c/code\u003e method as follows\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public static class Foo {\n\n @Red\n @Command\n public String fighters(final String arg) {\n return arg;\n }\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eFoo\u003c/span\u003e {\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eRed\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-en\"\u003efighters\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e;\n }\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003eRedInterceptor\u003c/code\u003e definition is as usual\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public class RedInterceptor {\n\n @CrestInterceptor\n public Object intercept(final CrestContext crestContext) {\n return crestContext.proceed();\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eRedInterceptor\u003c/span\u003e {\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCrestInterceptor\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eObject\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eintercept\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eCrestContext\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecrestContext\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecrestContext\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eproceed\u003c/span\u003e();\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 id=\"user-content-custom-annotation-containing-crestinterceptor-loosely-coupled-to-an-implementation\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eCustom annotation containing \u003ccode\u003e@CrestInterceptor\u003c/code\u003e loosely coupled to an implementation\u003c/h4\u003e\u003ca id=\"user-content-custom-annotation-containing-crestinterceptor-loosely-coupled-to-an-implementation\" class=\"anchor\" aria-label=\"Permalink: Custom annotation containing @CrestInterceptor loosely coupled to an implementation\" href=\"#custom-annotation-containing-crestinterceptor-loosely-coupled-to-an-implementation\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIn this style, we define our own custom annotation \u003ccode\u003e@Blue\u003c/code\u003e, but it is not bound to a specific implementation. The \u003ccode\u003e@CrestInterceptor\u003c/code\u003e does not mention the class.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@CrestInterceptor\n@Retention(value = RetentionPolicy.RUNTIME)\n@Target({ElementType.METHOD})\npublic @interface Blue {\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCrestInterceptor\u003c/span\u003e\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eRetention\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003evalue\u003c/span\u003e = \u003cspan class=\"pl-smi\"\u003eRetentionPolicy\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRUNTIME\u003c/span\u003e)\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eTarget\u003c/span\u003e({\u003cspan class=\"pl-smi\"\u003eElementType\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eMETHOD\u003c/span\u003e})\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e @interface \u003cspan class=\"pl-s1\"\u003eBlue\u003c/span\u003e {\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003e@Blue\u003c/code\u003e is used on our \u003ccode\u003e@Command\u003c/code\u003e method as in the previous example\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public static class Foo {\n\n @Blue\n @Command\n public String fighters(final String arg) {\n return arg;\n }\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eFoo\u003c/span\u003e {\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eBlue\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-en\"\u003efighters\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e;\n }\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003eBlueInterceptor\u003c/code\u003e definition identifies itself as the implementation of \u003ccode\u003e@Blue\u003c/code\u003e by using that annotation on its class\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Blue\npublic class BlueInterceptor {\n\n @CrestInterceptor\n public Object intercept(final CrestContext crestContext) {\n return crestContext.proceed();\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eBlue\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eBlueInterceptor\u003c/span\u003e {\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCrestInterceptor\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eObject\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eintercept\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eCrestContext\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecrestContext\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecrestContext\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eproceed\u003c/span\u003e();\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThis can be useful if you create an API jar where \u003ccode\u003e@Blue\u003c/code\u003e might be contained, but you want to put the implementation in a different jar. Perhaps there are different implementations, each it it’s own jar, and people choose the implementation they want by including the desired implementation jar in the classpath.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIn this approach, however, it is necessary to ensure \u003ccode\u003eBlueInterceptor.class\u003c/code\u003e is visible to Crest by creating a \u003ccode\u003eLoader\u003c/code\u003e implementation such as the following\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"package org.example.myapp;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\n\npublic class Loader implements org.tomitribe.crest.api.Loader {\n\n @Override\n public Iterator\u0026lt;Class\u0026lt;?\u0026gt;\u0026gt; iterator() {\n final List\u0026lt;Class\u0026lt;?\u0026gt;\u0026gt; classes = new ArrayList\u0026lt;\u0026gt;();\n classes.add(BlueInterceptor.class);\n return classes.listIterator();\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epackage\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eexample\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003emyapp\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eArrayList\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eArrays\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eCollections\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eIterator\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eList\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eLoader\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eimplements\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-smi\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-smi\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-smi\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-smi\"\u003eLoader\u003c/span\u003e {\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIterator\u003c/span\u003e\u0026lt;\u003cspan class=\"pl-smi\"\u003eClass\u003c/span\u003e\u0026lt;?\u0026gt;\u0026gt; \u003cspan class=\"pl-en\"\u003eiterator\u003c/span\u003e() {\n \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eList\u003c/span\u003e\u0026lt;\u003cspan class=\"pl-smi\"\u003eClass\u003c/span\u003e\u0026lt;?\u0026gt;\u0026gt; \u003cspan class=\"pl-s1\"\u003eclasses\u003c/span\u003e = \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eArrayList\u003c/span\u003e\u0026lt;\u0026gt;();\n \u003cspan class=\"pl-s1\"\u003eclasses\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eadd\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eBlueInterceptor\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e);\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eclasses\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003elistIterator\u003c/span\u003e();\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eand declaring it in the jar at \u003ccode\u003eMETA-INF/services/org.tomitribe.crest.api.Loader\u003c/code\u003e with the following contents:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"org.example.myapp.Loader\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003eorg.example.myapp.Loader\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 id=\"user-content-example-for-security\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eExample for security\u003c/h4\u003e\u003ca id=\"user-content-example-for-security\" class=\"anchor\" aria-label=\"Permalink: Example for security\" href=\"#example-for-security\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eCrest provides a \u003ccode\u003eorg.tomitribe.crest.interceptor.security.SecurityInterceptor\u003c/code\u003e which\nhandles \u003ccode\u003e@RolesAllowed\u003c/code\u003e using the SPI \u003ccode\u003eorg.tomitribe.crest.interceptor.security.RoleProvider\u003c/code\u003e to determine\nif you can call or not the command contextually.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eNote\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\n\u003ccode\u003eRoleProvider\u003c/code\u003e is taken from \u003ccode\u003eEnvironment\u003c/code\u003e services. You can register it through \u003ccode\u003eorg.tomitribe.crest.environments.SystemEnvironment\u003c/code\u003e constructor\nand just set it as environment on \u003ccode\u003eorg.tomitribe.crest.environments.Environment.ENVIRONMENT_THREAD_LOCAL\u003c/code\u003e.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eHere a sample command using it:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@RolesAllowed(\u0026quot;test\u0026quot;)\n@Command(interceptedBy = SecurityInterceptor.class)\npublic static String val() {\n return \u0026quot;ok\u0026quot;;\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eRolesAllowed\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"test\"\u003c/span\u003e)\n\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003einterceptedBy\u003c/span\u003e = \u003cspan class=\"pl-smi\"\u003eSecurityInterceptor\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e)\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eval\u003c/span\u003e() {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\"ok\"\u003c/span\u003e;\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-maven-archetype\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eMaven Archetype\u003c/h2\u003e\u003ca id=\"user-content-maven-archetype\" class=\"anchor\" aria-label=\"Permalink: Maven Archetype\" href=\"#maven-archetype\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eA maven archetype is available to quickly bootstrap small projects complete with the a pom like the above. Save yourself some time on copy/paste then find/replace.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cpre\u003emvn archetype:generate \\\n -DarchetypeGroupId=org.tomitribe \\\n -DarchetypeArtifactId=tomitribe-crest-archetype \\\n -DarchetypeVersion=1.0.0-SNAPSHOT\u003c/pre\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-maven-plugin\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eMaven Plugin\u003c/h2\u003e\u003ca id=\"user-content-maven-plugin\" class=\"anchor\" aria-label=\"Permalink: Maven Plugin\" href=\"#maven-plugin\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf you don’t want to rely on runtime scanning to find classes but still want to avoid to list command classes or just reuse crest Main\nyou can use Maven Plugin to find it and generate a descriptor used to load classes.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eHere is how to define it in your pom:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;plugin\u0026gt;\n \u0026lt;groupId\u0026gt;org.tomitribe\u0026lt;/groupId\u0026gt;\n \u0026lt;version\u0026gt;${crest.version}\u0026lt;/version\u0026gt;\n \u0026lt;artifactId\u0026gt;crest-maven-plugin\u0026lt;/artifactId\u0026gt;\n \u0026lt;executions\u0026gt;\n \u0026lt;execution\u0026gt;\n \u0026lt;goals\u0026gt;\n \u0026lt;goal\u0026gt;descriptor\u0026lt;/goal\u0026gt;\n \u0026lt;/goals\u0026gt;\n \u0026lt;/execution\u0026gt;\n \u0026lt;/executions\u0026gt;\n\u0026lt;/plugin\u0026gt;\"\u003e\u003cpre\u003e\u0026lt;\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.tomitribe\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;${crest.version}\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;crest-maven-plugin\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eexecutions\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eexecution\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egoals\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egoal\u003c/span\u003e\u0026gt;descriptor\u0026lt;/\u003cspan class=\"pl-ent\"\u003egoal\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003egoals\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eexecution\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eexecutions\u003c/span\u003e\u0026gt;\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-deltaspike-annotation-processor\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eDeltaSpike Annotation Processor\u003c/h2\u003e\u003ca id=\"user-content-deltaspike-annotation-processor\" class=\"anchor\" aria-label=\"Permalink: DeltaSpike Annotation Processor\" href=\"#deltaspike-annotation-processor\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eAdding this dependency to your project:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;dependency\u0026gt;\n \u0026lt;groupId\u0026gt;org.tomitribe\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;tomitribe-crest-generator\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;${crest.version}\u0026lt;/version\u0026gt;\n \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt;\n\u0026lt;/dependency\u0026gt;\"\u003e\u003cpre\u003e\u0026lt;\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.tomitribe\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;tomitribe-crest-generator\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;${crest.version}\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003escope\u003c/span\u003e\u0026gt;provided\u0026lt;/\u003cspan class=\"pl-ent\"\u003escope\u003c/span\u003e\u0026gt;\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eCrest Generator can integrates with DeltaSpike to generate binding pojo. It will split \u003ccode\u003e@ConfigProperty\u003c/code\u003e on first dot\nand create one binding per prefix.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eHere is an example:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"public class DeltaspikeBean {\n @Inject\n @ConfigProperty(name = \u0026quot;app.service.base\u0026quot;, defaultValue = \u0026quot;http://localhost:8080\u0026quot;)\n private String base;\n\n @Inject\n @ConfigProperty(name = \u0026quot;app.service.retries\u0026quot;)\n private Integer retries;\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eDeltaspikeBean\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eInject\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eConfigProperty\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"app.service.base\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003edefaultValue\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"http://localhost:8080\"\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003eprivate\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ebase\u003c/span\u003e;\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eInject\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eConfigProperty\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ename\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"app.service.retries\"\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003eprivate\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eInteger\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eretries\u003c/span\u003e;\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIt will generate the following binding:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"package org.tomitribe.crest.generator.generated;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.HashMap;\n\nimport org.apache.deltaspike.core.api.config.ConfigResolver;\nimport org.apache.deltaspike.core.spi.config.ConfigSource;\nimport org.tomitribe.crest.api.Default;\nimport org.tomitribe.crest.api.Option;\n\nimport static java.util.Collections.singletonList;\n\npublic class App {\n private String serviceBase;\n private Integer serviceRetries;\n\n public App(\n @Option(\u0026quot;service-base\u0026quot;) @Default(\u0026quot;http://localhost:8080\u0026quot;) String serviceBase,\n @Option(\u0026quot;service-retries\u0026quot;) Integer serviceRetries) {\n final Map\u0026lt;String, String\u0026gt; ____properties = new HashMap\u0026lt;\u0026gt;();\n this.serviceBase = serviceBase;\n ____properties.put(\u0026quot;app.service.base\u0026quot;, String.valueOf(serviceBase));\n this.serviceRetries = serviceRetries;\n ____properties.put(\u0026quot;app.service.retries\u0026quot;, String.valueOf(serviceRetries));\n ConfigResolver.addConfigSources(Collections.\u0026lt;ConfigSource\u0026gt;singletonList(new ConfigSource() {\n @Override\n public int getOrdinal() {\n return 0;\n }\n\n @Override\n public Map\u0026lt;String, String\u0026gt; getProperties() {\n return ____properties;\n }\n\n @Override\n public String getPropertyValue(final String key) {\n return ____properties.get(key);\n }\n\n @Override\n public String getConfigName() {\n return \u0026quot;crest-app\u0026quot;;\n }\n\n @Override\n public boolean isScannable() {\n return true;\n }\n })); }\n\n public String getServiceBase() {\n return serviceBase;\n }\n\n public void setServiceBase(final String serviceBase) {\n this.serviceBase = serviceBase;\n }\n\n public Integer getServiceRetries() {\n return serviceRetries;\n }\n\n public void setServiceRetries(final Integer serviceRetries) {\n this.serviceRetries = serviceRetries;\n }\n\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epackage\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003egenerator\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003egenerated\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eCollections\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eMap\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eHashMap\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapache\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003edeltaspike\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecore\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003econfig\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eConfigResolver\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapache\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003edeltaspike\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecore\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003espi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003econfig\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eConfigSource\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eDefault\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eOption\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eCollections\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003esingletonList\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eApp\u003c/span\u003e {\n \u003cspan class=\"pl-k\"\u003eprivate\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserviceBase\u003c/span\u003e;\n \u003cspan class=\"pl-k\"\u003eprivate\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eInteger\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserviceRetries\u003c/span\u003e;\n\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eApp\u003c/span\u003e(\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"service-base\"\u003c/span\u003e) \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eDefault\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"http://localhost:8080\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserviceBase\u003c/span\u003e,\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"service-retries\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eInteger\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserviceRetries\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eMap\u003c/span\u003e\u0026lt;\u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e\u0026gt; \u003cspan class=\"pl-s1\"\u003e____properties\u003c/span\u003e = \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eHashMap\u003c/span\u003e\u0026lt;\u0026gt;();\n \u003cspan class=\"pl-smi\"\u003ethis\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eserviceBase\u003c/span\u003e = \u003cspan class=\"pl-s1\"\u003eserviceBase\u003c/span\u003e;\n \u003cspan class=\"pl-s1\"\u003e____properties\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eput\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"app.service.base\"\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003evalueOf\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eserviceBase\u003c/span\u003e));\n \u003cspan class=\"pl-smi\"\u003ethis\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eserviceRetries\u003c/span\u003e = \u003cspan class=\"pl-s1\"\u003eserviceRetries\u003c/span\u003e;\n \u003cspan class=\"pl-s1\"\u003e____properties\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eput\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"app.service.retries\"\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003evalueOf\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eserviceRetries\u003c/span\u003e));\n \u003cspan class=\"pl-smi\"\u003eConfigResolver\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eaddConfigSources\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003eCollections\u003c/span\u003e.\u0026lt;\u003cspan class=\"pl-smi\"\u003eConfigSource\u003c/span\u003e\u0026gt;\u003cspan class=\"pl-en\"\u003esingletonList\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eConfigSource\u003c/span\u003e() {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-en\"\u003egetOrdinal\u003c/span\u003e() {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e;\n }\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eMap\u003c/span\u003e\u0026lt;\u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e\u0026gt; \u003cspan class=\"pl-en\"\u003egetProperties\u003c/span\u003e() {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003e____properties\u003c/span\u003e;\n }\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-en\"\u003egetPropertyValue\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ekey\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003e____properties\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eget\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ekey\u003c/span\u003e);\n }\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-en\"\u003egetConfigName\u003c/span\u003e() {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\"crest-app\"\u003c/span\u003e;\n }\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eboolean\u003c/span\u003e \u003cspan class=\"pl-en\"\u003eisScannable\u003c/span\u003e() {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003etrue\u003c/span\u003e;\n }\n })); }\n\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-en\"\u003egetServiceBase\u003c/span\u003e() {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserviceBase\u003c/span\u003e;\n }\n\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003esetServiceBase\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserviceBase\u003c/span\u003e) {\n \u003cspan class=\"pl-smi\"\u003ethis\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eserviceBase\u003c/span\u003e = \u003cspan class=\"pl-s1\"\u003eserviceBase\u003c/span\u003e;\n }\n\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eInteger\u003c/span\u003e \u003cspan class=\"pl-en\"\u003egetServiceRetries\u003c/span\u003e() {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserviceRetries\u003c/span\u003e;\n }\n\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003esetServiceRetries\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eInteger\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserviceRetries\u003c/span\u003e) {\n \u003cspan class=\"pl-smi\"\u003ethis\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eserviceRetries\u003c/span\u003e = \u003cspan class=\"pl-s1\"\u003eserviceRetries\u003c/span\u003e;\n }\n\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThen you just need to reuse it ad a crest command parameter:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"@Command\npublic void myCommand(@Option(\u0026quot;app-\u0026quot;) final App app) {\n // ...\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003emyCommand\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"app-\"\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eApp\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eapp\u003c/span\u003e) {\n \u003cspan class=\"pl-c\"\u003e// ...\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThe nice thing is it will integrate with crest of course but also with DeltaSpike. It means the previous code\nwill also make DeltaSpike injection respecting \u003ccode\u003eApp\u003c/code\u003e configuration (\u003ccode\u003e--app-service-base=… --app-service-retries=3\u003c/code\u003e for instance).\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eIf you create a fatjar using TomEE embedded it means you can handle all your DeltaSpike configuration this way\nand you just need to write a TomEE Embedded runner to get DeltaSpike configuration wired from the command line:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import org.apache.tomee.embedded.Main;\n\npublic final class Runner {\n @Command(\u0026quot;run\u0026quot;)\n public static void run(@Option(\u0026quot;app-\u0026quot;) App app) {\n Main.main(new String[] { \u0026quot;--as-war\u0026quot;, \u0026quot;--single-classloader\u0026quot; } /*fatjar \u0026quot;as war\u0026quot; deployment*/);\n // automatically @Inject @ConfigProperty will be populated :)\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapache\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomee\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eembedded\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eMain\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eRunner\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"run\"\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003evoid\u003c/span\u003e \u003cspan class=\"pl-en\"\u003erun\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOption\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"app-\"\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eApp\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eapp\u003c/span\u003e) {\n \u003cspan class=\"pl-smi\"\u003eMain\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003emain\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e[] { \u003cspan class=\"pl-s\"\u003e\"--as-war\"\u003c/span\u003e, \u003cspan class=\"pl-s\"\u003e\"--single-classloader\"\u003c/span\u003e } \u003cspan class=\"pl-c\"\u003e/*fatjar \"as war\" deployment*/\u003c/span\u003e);\n \u003cspan class=\"pl-c\"\u003e// automatically @Inject @ConfigProperty will be populated :)\u003c/span\u003e\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003ePotential enhancement(s):\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eoption to generate TomEE Embedded main?\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eTamaya integration on the same model?\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eOwner integration\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e…\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-cli-module\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eCli module\u003c/h2\u003e\u003ca id=\"user-content-cli-module\" class=\"anchor\" aria-label=\"Permalink: Cli module\" href=\"#cli-module\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eCli module aims to provide a basic integration with JLine.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eAll starts from \u003ccode\u003eorg.tomitribe.crest.cli.api.CrestCli\u003c/code\u003e class. Current version is extensible through inheritance but already provides:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003esupport of maven plugin commands (crest-commands.txt)\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eJLine integration\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eBasic pipping support (\u003ccode\u003emycommand | jgrep foo\u003c/code\u003e)\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eHistory support is you return a file in \u003ccode\u003eorg.tomitribe.crest.cli.api.CrestCli.cliHistoryFile\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eorg.tomitribe.crest.cli.api.interceptor.interactive.Interactivable\u003c/code\u003e can be used to mark a parameter as required but compatible with interactive mode\n(ie the parameter is asked in interactive mode if missing).\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eSample usage:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"final CrestCli cli = new CrestCli();\ncli.run();\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eCrestCli\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecli\u003c/span\u003e = \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eCrestCli\u003c/span\u003e();\n\u003cspan class=\"pl-s1\"\u003ecli\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003erun\u003c/span\u003e();\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eTip\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\n\u003ccode\u003eCrestCli\u003c/code\u003e also has a \u003ccode\u003emain(String[])\u003c/code\u003e so it can be used directly as well.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eNote\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\nif you don’t provide an \u003ccode\u003eexit\u003c/code\u003e command one is added by default.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 id=\"user-content-graalvm-integration\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGraalVM integration\u003c/h2\u003e\u003ca id=\"user-content-graalvm-integration\" class=\"anchor\" aria-label=\"Permalink: GraalVM integration\" href=\"#graalvm-integration\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTomitribe Crest works very smoothly with GraalVM enabling you to get a native binary from your CLI.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eYou can do it writing manually your \u003ccode\u003ereflections.json\u003c/code\u003e but you can also do it through maven using \u003ccode\u003eApache Geronimo Arthur\u003c/code\u003e plugin.\nIn this last case, you can set up your CLI auto-configuration with this setup:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eImportant\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\nrequires Tomitribe Crest \u0026gt;= 0.17.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;plugin\u0026gt;\n \u0026lt;groupId\u0026gt;org.apache.geronimo.arthur\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;arthur-maven-plugin\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;1.0.3\u0026lt;/version\u0026gt;\n \u0026lt;configuration\u0026gt;\n \u0026lt;graalVersion\u0026gt;21.3.0.r17\u0026lt;/graalVersion\u0026gt; (1)\n \u0026lt;main\u0026gt;org.tomitribe.crest.Main\u0026lt;/main\u0026gt; (2)\n \u0026lt;extensionProperties\u0026gt; (4)\n \u0026lt;!-- starts with, excludes exists too, don't forget help if you don't override it yourself --\u0026gt;\n \u0026lt;tomitribe.crest.command.includes\u0026gt;\n com.superbiz.command,\n org.tomitribe.crest.cmds.processors.Help\n \u0026lt;/tomitribe.crest.command.includes\u0026gt;\n \u0026lt;!-- \u0026lt;tomitribe.crest.editors.includes\u0026gt;....\u0026lt;/tomitribe.crest.editors.includes\u0026gt; --\u0026gt; (5)\n \u0026lt;/extensionProperties\u0026gt;\n \u0026lt;enableAllSecurityServices\u0026gt;false\u0026lt;/enableAllSecurityServices\u0026gt; (6)\n \u0026lt;/configuration\u0026gt;\n \u0026lt;dependencies\u0026gt; (3)\n \u0026lt;!-- enable crest auto registration for commands/interceptors --\u0026gt;\n \u0026lt;dependencies\u0026gt;\n \u0026lt;dependency\u0026gt;\n \u0026lt;groupId\u0026gt;org.tomitribe\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;tomitribe-crest-arthur-extension\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;${crest.version}\u0026lt;/version\u0026gt;\n \u0026lt;/dependency\u0026gt;\n \u0026lt;/dependencies\u0026gt;\n \u0026lt;/dependencies\u0026gt;\n\u0026lt;/plugin\u0026gt;\"\u003e\u003cpre\u003e\u0026lt;\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.apache.geronimo.arthur\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;arthur-maven-plugin\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;1.0.3\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egraalVersion\u003c/span\u003e\u0026gt;21.3.0.r17\u0026lt;/\u003cspan class=\"pl-ent\"\u003egraalVersion\u003c/span\u003e\u0026gt; (1)\n \u0026lt;\u003cspan class=\"pl-ent\"\u003emain\u003c/span\u003e\u0026gt;org.tomitribe.crest.Main\u0026lt;/\u003cspan class=\"pl-ent\"\u003emain\u003c/span\u003e\u0026gt; (2)\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eextensionProperties\u003c/span\u003e\u0026gt; (4)\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e starts with, excludes exists too, don't forget help if you don't override it yourself \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etomitribe\u003c/span\u003e.crest.command.includes\u0026gt;\n com.superbiz.command,\n org.tomitribe.crest.cmds.processors.Help\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003etomitribe\u003c/span\u003e.crest.command.includes\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e \u0026lt;tomitribe.crest.editors.includes\u0026gt;....\u0026lt;/tomitribe.crest.editors.includes\u0026gt; \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e (5)\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eextensionProperties\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eenableAllSecurityServices\u003c/span\u003e\u0026gt;false\u0026lt;/\u003cspan class=\"pl-ent\"\u003eenableAllSecurityServices\u003c/span\u003e\u0026gt; (6)\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt; (3)\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e enable crest auto registration for commands/interceptors \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.tomitribe\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;tomitribe-crest-arthur-extension\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;${crest.version}\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eEnsure to adjust the Graal and JVM base version (here Graal 21.3.0 in its Java 17 flavor),\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eReuse default Crest main,\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eEnable crest extension for Arthur,\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eCustomize the command scanning, note that you can tune the includes/excludes and the values are comma separated and use a \"start with\" matching logic,\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eIf you are using \u003ccode\u003e@Editor\u003c/code\u003e, you can control the scanning there too similarly to commands,\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003eThis option is deprecated in recent graal versions so avoid a warning using a recent version, no direct link with crest itself,\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThen just run: \u003ccode\u003emvn install arthur:native-image\u003c/code\u003e and you will get a \u003ccode\u003etarget/\u0026lt;artifctId\u0026gt;.graal.bin\u003c/code\u003e binary you can share and execute on the built platform.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv dir=\"auto\"\u003eImportant\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\nonly scanned editors (\u003ccode\u003e@Editor\u003c/code\u003e) are handled by the extension, SPI ones (\u003ccode\u003eMETA-INF/services/org.tomitribe.crest.api.Editor\u003c/code\u003e) can be used if you register them within GraalVM configuration and enable \u003ccode\u003eServiceLoader\u003c/code\u003e.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 id=\"user-content-graalvm-example\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGraalVM example\u003c/h3\u003e\u003ca id=\"user-content-graalvm-example\" class=\"anchor\" aria-label=\"Permalink: GraalVM example\" href=\"#graalvm-example\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003eCat.java\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"package org.superbiz.crest.demo;\n\nimport org.tomitribe.crest.api.Command;\nimport org.tomitribe.crest.api.Editor;\nimport org.tomitribe.util.editor.AbstractConverter;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\npublic class Cat {\n @Command(usage = \u0026quot;Cat a file.\u0026quot;)\n public String cat(final Path file) throws IOException {\n return Files.readString(file);\n }\n\n @Editor(Path.class)\n public static class PathEditor extends AbstractConverter {\n @Override\n protected Object toObjectImpl(final String text) {\n return Paths.get(text);\n }\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003epackage\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003esuperbiz\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003edemo\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eCommand\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ecrest\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eapi\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eEditor\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eorg\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003etomitribe\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eutil\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eeditor\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eAbstractConverter\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eio\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eIOException\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003enio\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003eFiles\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003enio\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ePath\u003c/span\u003e;\n\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ejava\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003enio\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e.\u003cspan class=\"pl-s1\"\u003ePaths\u003c/span\u003e;\n\n\u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eCat\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eCommand\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eusage\u003c/span\u003e = \u003cspan class=\"pl-s\"\u003e\"Cat a file.\"\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-en\"\u003ecat\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ePath\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003ethrows\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eIOException\u003c/span\u003e {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eFiles\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003ereadString\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003efile\u003c/span\u003e);\n }\n\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eEditor\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003ePath\u003c/span\u003e.\u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003epublic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estatic\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eclass\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ePathEditor\u003c/span\u003e \u003cspan class=\"pl-k\"\u003eextends\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eAbstractConverter\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003e@\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003eOverride\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003eprotected\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eObject\u003c/span\u003e \u003cspan class=\"pl-en\"\u003etoObjectImpl\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efinal\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eString\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etext\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ePaths\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eget\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etext\u003c/span\u003e);\n }\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003epom.xml\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;?xml version=\u0026quot;1.0\u0026quot; encoding=\u0026quot;UTF-8\u0026quot;?\u0026gt;\n\u0026lt;project xmlns=\u0026quot;http://maven.apache.org/POM/4.0.0\u0026quot;\n xmlns:xsi=\u0026quot;http://www.w3.org/2001/XMLSchema-instance\u0026quot;\n xsi:schemaLocation=\u0026quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\u0026quot;\u0026gt;\n \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\n \u0026lt;groupId\u0026gt;org.superbiz.demo\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;demo-crest-arthur\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt;\n\n \u0026lt;properties\u0026gt;\n \u0026lt;crest.version\u0026gt;...\u0026lt;/crest.version\u0026gt; \u0026lt;!-- \u0026gt;= 0.17 --\u0026gt;\n \u0026lt;/properties\u0026gt;\n\n \u0026lt;dependencies\u0026gt;\n \u0026lt;dependency\u0026gt;\n \u0026lt;groupId\u0026gt;org.tomitribe\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;tomitribe-crest\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;${crest.vesion}\u0026lt;/version\u0026gt;\n \u0026lt;/dependency\u0026gt;\n \u0026lt;/dependencies\u0026gt;\n\n \u0026lt;build\u0026gt;\n \u0026lt;plugins\u0026gt;\n \u0026lt;plugin\u0026gt;\n \u0026lt;groupId\u0026gt;org.apache.maven.plugins\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;maven-resources-plugin\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;3.2.0\u0026lt;/version\u0026gt;\n \u0026lt;configuration\u0026gt;\n \u0026lt;encoding\u0026gt;UTF-8\u0026lt;/encoding\u0026gt;\n \u0026lt;/configuration\u0026gt;\n \u0026lt;/plugin\u0026gt;\n \u0026lt;plugin\u0026gt;\n \u0026lt;groupId\u0026gt;org.apache.maven.plugins\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;maven-compiler-plugin\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;3.8.1\u0026lt;/version\u0026gt;\n \u0026lt;configuration\u0026gt;\n \u0026lt;release\u0026gt;17\u0026lt;/release\u0026gt;\n \u0026lt;source\u0026gt;17\u0026lt;/source\u0026gt;\n \u0026lt;target\u0026gt;17\u0026lt;/target\u0026gt;\n \u0026lt;/configuration\u0026gt;\n \u0026lt;/plugin\u0026gt;\n \u0026lt;plugin\u0026gt;\n \u0026lt;groupId\u0026gt;org.apache.geronimo.arthur\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;arthur-maven-plugin\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;1.0.3\u0026lt;/version\u0026gt;\n \u0026lt;configuration\u0026gt;\n \u0026lt;graalVersion\u0026gt;21.3.0.r17\u0026lt;/graalVersion\u0026gt;\n \u0026lt;main\u0026gt;org.tomitribe.crest.Main\u0026lt;/main\u0026gt;\n \u0026lt;graalExtensions\u0026gt;\n \u0026lt;!-- enable crest auto registration for commands/interceptors --\u0026gt;\n \u0026lt;graalExtension\u0026gt;org.tomitribe:tomitribe-crest-arthur-extension:0.17-SNAPSHOT\u0026lt;/graalExtension\u0026gt;\n \u0026lt;/graalExtensions\u0026gt;\n \u0026lt;extensionProperties\u0026gt;\n \u0026lt;!-- starts with, excludes exists too --\u0026gt;\n \u0026lt;tomitribe.crest.command.includes\u0026gt;\n ${project.groupId}.,\n org.tomitribe.crest.cmds.processors.Help\n \u0026lt;/tomitribe.crest.command.includes\u0026gt;\n \u0026lt;/extensionProperties\u0026gt;\n \u0026lt;!-- this option is deprecated in recent graal versions --\u0026gt;\n \u0026lt;enableAllSecurityServices\u0026gt;false\u0026lt;/enableAllSecurityServices\u0026gt;\n \u0026lt;/configuration\u0026gt;\n \u0026lt;dependencies\u0026gt;\n \u0026lt;dependency\u0026gt; \u0026lt;!-- force arthur to support java 17 --\u0026gt;\n \u0026lt;groupId\u0026gt;org.apache.xbean\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;xbean-asm9-shaded\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;4.20\u0026lt;/version\u0026gt;\n \u0026lt;/dependency\u0026gt;\n \u0026lt;/dependencies\u0026gt;\n \u0026lt;/plugin\u0026gt;\n \u0026lt;/plugins\u0026gt;\n \u0026lt;/build\u0026gt;\n\u0026lt;/project\u0026gt;\"\u003e\u003cpre\u003e\u0026lt;?\u003cspan class=\"pl-ent\"\u003exml\u003c/span\u003e\u003cspan class=\"pl-e\"\u003e version\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e1.0\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"pl-e\"\u003e encoding\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eUTF-8\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e?\u0026gt;\n\u0026lt;\u003cspan class=\"pl-ent\"\u003eproject\u003c/span\u003e \u003cspan class=\"pl-e\"\u003exmlns\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ehttp://maven.apache.org/POM/4.0.0\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\n \u003cspan class=\"pl-e\"\u003exmlns\u003c/span\u003e\u003cspan class=\"pl-e\"\u003e:\u003c/span\u003e\u003cspan class=\"pl-e\"\u003exsi\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ehttp://www.w3.org/2001/XMLSchema-instance\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\n \u003cspan class=\"pl-e\"\u003exsi\u003c/span\u003e\u003cspan class=\"pl-e\"\u003e:\u003c/span\u003e\u003cspan class=\"pl-e\"\u003eschemaLocation\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ehttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003emodelVersion\u003c/span\u003e\u0026gt;4.0.0\u0026lt;/\u003cspan class=\"pl-ent\"\u003emodelVersion\u003c/span\u003e\u0026gt;\n\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.superbiz.demo\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;demo-crest-arthur\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;1.0-SNAPSHOT\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eproperties\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003ecrest\u003c/span\u003e.version\u0026gt;...\u0026lt;/\u003cspan class=\"pl-ent\"\u003ecrest\u003c/span\u003e.version\u0026gt; \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e \u0026gt;= 0.17 \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eproperties\u003c/span\u003e\u0026gt;\n\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.tomitribe\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;tomitribe-crest\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;${crest.vesion}\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n\n \u0026lt;\u003cspan class=\"pl-ent\"\u003ebuild\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eplugins\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.apache.maven.plugins\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;maven-resources-plugin\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;3.2.0\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eencoding\u003c/span\u003e\u0026gt;UTF-8\u0026lt;/\u003cspan class=\"pl-ent\"\u003eencoding\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.apache.maven.plugins\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;maven-compiler-plugin\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;3.8.1\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003erelease\u003c/span\u003e\u0026gt;17\u0026lt;/\u003cspan class=\"pl-ent\"\u003erelease\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003esource\u003c/span\u003e\u0026gt;17\u0026lt;/\u003cspan class=\"pl-ent\"\u003esource\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etarget\u003c/span\u003e\u0026gt;17\u0026lt;/\u003cspan class=\"pl-ent\"\u003etarget\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.apache.geronimo.arthur\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;arthur-maven-plugin\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;1.0.3\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egraalVersion\u003c/span\u003e\u0026gt;21.3.0.r17\u0026lt;/\u003cspan class=\"pl-ent\"\u003egraalVersion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003emain\u003c/span\u003e\u0026gt;org.tomitribe.crest.Main\u0026lt;/\u003cspan class=\"pl-ent\"\u003emain\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egraalExtensions\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e enable crest auto registration for commands/interceptors \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egraalExtension\u003c/span\u003e\u0026gt;org.tomitribe:tomitribe-crest-arthur-extension:0.17-SNAPSHOT\u0026lt;/\u003cspan class=\"pl-ent\"\u003egraalExtension\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003egraalExtensions\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eextensionProperties\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e starts with, excludes exists too \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etomitribe\u003c/span\u003e.crest.command.includes\u0026gt;\n ${project.groupId}.,\n org.tomitribe.crest.cmds.processors.Help\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003etomitribe\u003c/span\u003e.crest.command.includes\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eextensionProperties\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e this option is deprecated in recent graal versions \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eenableAllSecurityServices\u003c/span\u003e\u0026gt;false\u0026lt;/\u003cspan class=\"pl-ent\"\u003eenableAllSecurityServices\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt; \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e force arthur to support java 17 \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.apache.xbean\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;xbean-asm9-shaded\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;4.20\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugins\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003ebuild\u003c/span\u003e\u0026gt;\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003eproject\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eOnce this project created, you can run \u003ccode\u003emvn clean install arthur:native-image\u003c/code\u003e.\nThis creates a \u003ccode\u003e./target/demo-crest-arthur.graal.bin\u003c/code\u003e binary and you can execute \u003ccode\u003ecat\u003c/code\u003e command using: \u003ccode\u003e./target/demo-crest-arthur.graal.bin cat \u0026lt;some file\u0026gt;\u003c/code\u003e.\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 id=\"user-content-use-crest-maven-plugin-scanning\" tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eUse Crest Maven Plugin Scanning\u003c/h3\u003e\u003ca id=\"user-content-use-crest-maven-plugin-scanning\" class=\"anchor\" aria-label=\"Permalink: Use Crest Maven Plugin Scanning\" href=\"#use-crest-maven-plugin-scanning\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eit is also possible to make Arthur extension use \u003ccode\u003ecrest-maven-plugin\u003c/code\u003e scan goal (\u003ccode\u003edescriptor\u003c/code\u003e).\nJust set extension property \u003ccode\u003etomitribe.crest.useInPlaceRegistrations\u003c/code\u003e to \u003ccode\u003etrue\u003c/code\u003e:\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;plugin\u0026gt;\n \u0026lt;groupId\u0026gt;org.tomitribe\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;crest-maven-plugin\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;${crest.version}\u0026lt;/version\u0026gt;\n \u0026lt;executions\u0026gt;\n \u0026lt;execution\u0026gt;\n \u0026lt;id\u0026gt;scan\u0026lt;/id\u0026gt;\n \u0026lt;phase\u0026gt;process-classes\u0026lt;/phase\u0026gt;\n \u0026lt;goals\u0026gt;\n \u0026lt;goal\u0026gt;descriptor\u0026lt;/goal\u0026gt;\n \u0026lt;/goals\u0026gt;\n \u0026lt;/execution\u0026gt;\n \u0026lt;/executions\u0026gt;\n\u0026lt;/plugin\u0026gt;\n\u0026lt;plugin\u0026gt;\n \u0026lt;groupId\u0026gt;org.apache.geronimo.arthur\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;arthur-maven-plugin\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;1.0.3\u0026lt;/version\u0026gt;\n \u0026lt;configuration\u0026gt;\n \u0026lt;graalVersion\u0026gt;21.3.0.r17\u0026lt;/graalVersion\u0026gt;\n \u0026lt;main\u0026gt;org.tomitribe.crest.Main\u0026lt;/main\u0026gt;\n \u0026lt;graalExtensions\u0026gt;\n \u0026lt;graalExtension\u0026gt;org.tomitribe:tomitribe-crest-arthur-extension:0.17-SNAPSHOT\u0026lt;/graalExtension\u0026gt;\n \u0026lt;/graalExtensions\u0026gt;\n \u0026lt;extensionProperties\u0026gt;\n \u0026lt;!-- reuse crest maven plugin scanning --\u0026gt;\n \u0026lt;tomitribe.crest.useInPlaceRegistrations\u0026gt;true\u0026lt;/tomitribe.crest.useInPlaceRegistrations\u0026gt;\n \u0026lt;/extensionProperties\u0026gt;\n \u0026lt;enableAllSecurityServices\u0026gt;false\u0026lt;/enableAllSecurityServices\u0026gt;\n \u0026lt;/configuration\u0026gt;\n \u0026lt;dependencies\u0026gt;\n \u0026lt;dependency\u0026gt; \u0026lt;!-- force arthur to support java 17 --\u0026gt;\n \u0026lt;groupId\u0026gt;org.apache.xbean\u0026lt;/groupId\u0026gt;\n \u0026lt;artifactId\u0026gt;xbean-asm9-shaded\u0026lt;/artifactId\u0026gt;\n \u0026lt;version\u0026gt;4.20\u0026lt;/version\u0026gt;\n \u0026lt;/dependency\u0026gt;\n \u0026lt;/dependencies\u0026gt;\n\u0026lt;/plugin\u0026gt;\"\u003e\u003cpre\u003e\u0026lt;\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.tomitribe\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;crest-maven-plugin\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;${crest.version}\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eexecutions\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eexecution\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eid\u003c/span\u003e\u0026gt;scan\u0026lt;/\u003cspan class=\"pl-ent\"\u003eid\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003ephase\u003c/span\u003e\u0026gt;process-classes\u0026lt;/\u003cspan class=\"pl-ent\"\u003ephase\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egoals\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egoal\u003c/span\u003e\u0026gt;descriptor\u0026lt;/\u003cspan class=\"pl-ent\"\u003egoal\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003egoals\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eexecution\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eexecutions\u003c/span\u003e\u0026gt;\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n\u0026lt;\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.apache.geronimo.arthur\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;arthur-maven-plugin\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;1.0.3\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egraalVersion\u003c/span\u003e\u0026gt;21.3.0.r17\u0026lt;/\u003cspan class=\"pl-ent\"\u003egraalVersion\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003emain\u003c/span\u003e\u0026gt;org.tomitribe.crest.Main\u0026lt;/\u003cspan class=\"pl-ent\"\u003emain\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egraalExtensions\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egraalExtension\u003c/span\u003e\u0026gt;org.tomitribe:tomitribe-crest-arthur-extension:0.17-SNAPSHOT\u0026lt;/\u003cspan class=\"pl-ent\"\u003egraalExtension\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003egraalExtensions\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eextensionProperties\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e reuse crest maven plugin scanning \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etomitribe\u003c/span\u003e.crest.useInPlaceRegistrations\u0026gt;true\u0026lt;/\u003cspan class=\"pl-ent\"\u003etomitribe\u003c/span\u003e.crest.useInPlaceRegistrations\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eextensionProperties\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eenableAllSecurityServices\u003c/span\u003e\u0026gt;false\u0026lt;/\u003cspan class=\"pl-ent\"\u003eenableAllSecurityServices\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003econfiguration\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt; \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e force arthur to support java 17 \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;org.apache.xbean\u0026lt;/\u003cspan class=\"pl-ent\"\u003egroupId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;xbean-asm9-shaded\u0026lt;/\u003cspan class=\"pl-ent\"\u003eartifactId\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;4.20\u0026lt;/\u003cspan class=\"pl-ent\"\u003eversion\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependency\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003edependencies\u003c/span\u003e\u0026gt;\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003eplugin\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eThis enables to use the same scanning for both tasks and therefore to have a common and unified scanning for java and native runs.\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c/article\u003e","loaded":true,"timedOut":false,"errorMessage":null,"headerInfo":{"toc":[{"level":1,"text":"CREST","anchor":"crest","htmlText":"CREST"},{"level":2,"text":"Start your project","anchor":"start-your-project","htmlText":"Start your project"},{"level":2,"text":"Example: rsync as a Crest command","anchor":"example-rsync-as-a-crest-command","htmlText":"Example: rsync as a Crest command"},{"level":3,"text":"Executing the Command","anchor":"executing-the-command","htmlText":"Executing the Command"},{"level":2,"text":"Help Text","anchor":"help-text","htmlText":"Help Text"},{"level":2,"text":"Bash Completion","anchor":"bash-completion","htmlText":"Bash Completion"},{"level":2,"text":"@Default values","anchor":"default-values","htmlText":"@Default values"},{"level":3,"text":"Advanced","anchor":"advanced","htmlText":"Advanced"},{"level":2,"text":"@Option Lists and Arrays","anchor":"option-lists-and-arrays","htmlText":"@Option Lists and Arrays"},{"level":2,"text":"@Default @Option Lists and Arrays","anchor":"default-option-lists-and-arrays","htmlText":"@Default @Option Lists and Arrays"},{"level":2,"text":"@Default and ${variable} Substitution","anchor":"default-and-variable-substitution","htmlText":"@Default and ${variable} Substitution"},{"level":2,"text":"Return Values","anchor":"return-values","htmlText":"Return Values"},{"level":2,"text":"Stream injections","anchor":"stream-injections","htmlText":"Stream injections"},{"level":2,"text":"Custom Java Types","anchor":"custom-java-types","htmlText":"Custom Java Types"},{"level":2,"text":"Custom Validation","anchor":"custom-validation","htmlText":"Custom Validation"},{"level":3,"text":"Bean Validation-less validations","anchor":"bean-validation-less-validations","htmlText":"Bean Validation-less validations"},{"level":2,"text":"Maven pom.xml setup","anchor":"maven-pomxml-setup","htmlText":"Maven pom.xml setup"},{"level":2,"text":"Bean Parameter Binding","anchor":"bean-parameter-binding","htmlText":"Bean Parameter Binding"},{"level":3,"text":"Prefixing options","anchor":"prefixing-options","htmlText":"Prefixing options"},{"level":3,"text":"Override defaults","anchor":"override-defaults","htmlText":"Override defaults"},{"level":3,"text":"Interceptors","anchor":"interceptors","htmlText":"Interceptors"},{"level":4,"text":"Via @Command(interceptedBy)","anchor":"via-commandinterceptedby","htmlText":"Via @Command(interceptedBy)"},{"level":4,"text":"Custom annotation containing @CrestInterceptor(FooInterceptor.class)","anchor":"custom-annotation-containing-crestinterceptorfoointerceptorclass","htmlText":"Custom annotation containing @CrestInterceptor(FooInterceptor.class)"},{"level":4,"text":"Custom annotation containing @CrestInterceptor loosely coupled to an implementation","anchor":"custom-annotation-containing-crestinterceptor-loosely-coupled-to-an-implementation","htmlText":"Custom annotation containing @CrestInterceptor loosely coupled to an implementation"},{"level":4,"text":"Example for security","anchor":"example-for-security","htmlText":"Example for security"},{"level":2,"text":"Maven Archetype","anchor":"maven-archetype","htmlText":"Maven Archetype"},{"level":2,"text":"Maven Plugin","anchor":"maven-plugin","htmlText":"Maven Plugin"},{"level":2,"text":"DeltaSpike Annotation Processor","anchor":"deltaspike-annotation-processor","htmlText":"DeltaSpike Annotation Processor"},{"level":2,"text":"Cli module","anchor":"cli-module","htmlText":"Cli module"},{"level":2,"text":"GraalVM integration","anchor":"graalvm-integration","htmlText":"GraalVM integration"},{"level":3,"text":"GraalVM example","anchor":"graalvm-example","htmlText":"GraalVM example"},{"level":3,"text":"Use Crest Maven Plugin Scanning","anchor":"use-crest-maven-plugin-scanning","htmlText":"Use Crest Maven Plugin Scanning"}],"siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2Ftomitribe%2Fcrest"}},{"displayName":"LICENSE","repoName":"crest","refName":"master","path":"LICENSE","preferredFileType":"license","tabName":"Apache-2.0","richText":null,"loaded":false,"timedOut":false,"errorMessage":null,"headerInfo":{"toc":null,"siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2Ftomitribe%2Fcrest"}}],"overviewFilesProcessingTime":0}},"appPayload":{"helpUrl":"https://docs.github.com","findFileWorkerPath":"/assets-cdn/worker/find-file-worker-7d7eb7c71814.js","findInFileWorkerPath":"/assets-cdn/worker/find-in-file-worker-708ec8ade250.js","githubDevUrl":null,"enabled_features":{"copilot_workspace":null,"code_nav_ui_events":false,"overview_shared_code_dropdown_button":true,"react_blob_overlay":false,"accessible_code_button":true,"github_models_repo_integration":false}}}}</script> <div data-target="react-partial.reactRoot"><style data-styled="true" data-styled-version="5.3.11">.iVEunk{margin-top:16px;margin-bottom:16px;}/*!sc*/ .jzuOtQ{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;}/*!sc*/ .bGojzy{margin-bottom:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;row-gap:16px;}/*!sc*/ .iNSVHo{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;padding-bottom:16px;padding-top:8px;}/*!sc*/ .bVgnfw{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;gap:8px;}/*!sc*/ @media screen and (max-width:320px){.bVgnfw{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}}/*!sc*/ .CEgMp{position:relative;}/*!sc*/ @media screen and (max-width:380px){.CEgMp .ref-selector-button-text-container{max-width:80px;}}/*!sc*/ @media screen and (max-width:320px){.CEgMp{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}.CEgMp .overview-ref-selector{width:100%;}.CEgMp .overview-ref-selector > span{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;}.CEgMp .overview-ref-selector > span > span[data-component="text"]{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}}/*!sc*/ .gMOVLe[data-size="medium"]{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:0;}/*!sc*/ .gMOVLe[data-size="medium"] svg{color:var(--fgColor-muted,var(--color-fg-muted,#656d76));}/*!sc*/ .gMOVLe[data-size="medium"] > span{width:inherit;}/*!sc*/ .gUkoLg{-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}/*!sc*/ .bZBlpz{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;}/*!sc*/ .lhTYNA{margin-right:4px;color:var(--fgColor-muted,var(--color-fg-muted,#656d76));}/*!sc*/ .ffLUq{font-size:14px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}/*!sc*/ .bmcJak{min-width:0;}/*!sc*/ .fLXEGX{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (max-width:1079px){.fLXEGX{display:none;}}/*!sc*/ .lmSMZJ[data-size="medium"]{color:var(--fgColor-muted,var(--color-fg-muted,#656d76));padding-left:4px;padding-right:4px;}/*!sc*/ .lmSMZJ[data-size="medium"] span[data-component="leadingVisual"]{margin-right:4px !important;}/*!sc*/ .dqfxud{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (min-width:1080px){.dqfxud{display:none;}}/*!sc*/ @media screen and (max-width:543px){.dqfxud{display:none;}}/*!sc*/ .fGwBZA[data-size="medium"][data-no-visuals]{color:var(--fgColor-muted,var(--color-fg-muted,#656d76));}/*!sc*/ .jxTzTd{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding-left:8px;gap:8px;}/*!sc*/ .gqqBXN{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:8px;}/*!sc*/ @media screen and (max-width:543px){.gqqBXN{display:none;}}/*!sc*/ .dzXgxt{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (max-width:1011px){.dzXgxt{display:none;}}/*!sc*/ .iWFGlI{margin-left:8px;margin-right:8px;margin:0;}/*!sc*/ .vcvyP{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:160px;}/*!sc*/ .YUPas{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (min-width:1012px){.YUPas{display:none;}}/*!sc*/ .izFOf{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (min-width:544px){.izFOf{display:none;}}/*!sc*/ .vIPPs{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:16px;}/*!sc*/ .fdROMU{width:100%;border-collapse:separate;border-spacing:0;border:1px solid;border-color:var(--borderColor-default,var(--color-border-default,#d0d7de));border-radius:6px;table-layout:fixed;overflow:unset;}/*!sc*/ .jGKpsv{height:0px;line-height:0px;}/*!sc*/ .jGKpsv tr{height:0px;font-size:0px;}/*!sc*/ .jdgHnn{padding:16px;color:var(--fgColor-muted,var(--color-fg-muted,#656d76));font-size:12px;text-align:left;height:40px;}/*!sc*/ .jdgHnn th{padding-left:16px;background-color:var(--bgColor-muted,var(--color-canvas-subtle,#f6f8fa));}/*!sc*/ .bQivRW{width:100%;border-top-left-radius:6px;}/*!sc*/ @media screen and (min-width:544px){.bQivRW{display:none;}}/*!sc*/ .ldkMIO{width:40%;border-top-left-radius:6px;}/*!sc*/ @media screen and (max-width:543px){.ldkMIO{display:none;}}/*!sc*/ .jMbWeI{text-align:right;padding-right:16px;width:136px;border-top-right-radius:6px;}/*!sc*/ .gpqjiB{color:var(--fgColor-muted,var(--color-fg-muted,#656d76));font-size:12px;height:40px;}/*!sc*/ .dzCJzi{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:8px;min-width:273px;padding:8px;}/*!sc*/ @media screen and (min-width:544px){.dzCJzi{-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;}}/*!sc*/ .eNCcrz{text-align:center;vertical-align:center;height:40px;border-top:1px solid;border-color:var(--borderColor-default,var(--color-border-default,#d0d7de));}/*!sc*/ .bHTcCe{border-top:1px solid var(--borderColor-default,var(--color-border-default));cursor:pointer;}/*!sc*/ .csrIcr{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;gap:16px;}/*!sc*/ .bUQNHB{border:1px solid;border-color:var(--borderColor-default,var(--color-border-default,#d0d7de));border-radius:6px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}/*!sc*/ @media screen and (max-width:543px){.bUQNHB{margin-left:-16px;margin-right:-16px;max-width:calc(100% + 32px);}}/*!sc*/ @media screen and (min-width:544px){.bUQNHB{max-width:100%;}}/*!sc*/ .jPdcfu{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;border-bottom:1px solid;border-bottom-color:var(--borderColor-default,var(--color-border-default,#d0d7de));-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-right:8px;position:-webkit-sticky;position:sticky;top:0;background-color:var(--bgColor-default,var(--color-canvas-default,#ffffff));z-index:1;border-top-left-radius:6px;border-top-right-radius:6px;}/*!sc*/ .iphEWz{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;border-bottom:none;max-width:100%;padding-left:8px;padding-right:8px;}/*!sc*/ .hUCRAk{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}/*!sc*/ .cwoBXV[data-size="medium"]{color:var(--fgColor-muted,var(--color-fg-subtle,#6e7781));padding-left:8px;padding-right:8px;}/*!sc*/ .QkQOb{padding:32px;overflow:auto;}/*!sc*/ data-styled.g1[id="Box-sc-g0xbh4-0"]{content:"iVEunk,jzuOtQ,bGojzy,iNSVHo,bVgnfw,CEgMp,gMOVLe,gUkoLg,bZBlpz,lhTYNA,ffLUq,bmcJak,fLXEGX,lmSMZJ,dqfxud,fGwBZA,jxTzTd,gqqBXN,dzXgxt,iWFGlI,vcvyP,YUPas,izFOf,vIPPs,fdROMU,jGKpsv,jdgHnn,bQivRW,ldkMIO,jMbWeI,gpqjiB,dzCJzi,eNCcrz,bHTcCe,csrIcr,bUQNHB,jPdcfu,iphEWz,hUCRAk,cwoBXV,QkQOb,"}/*!sc*/ .brGdpi{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;-webkit-clip:rect(0,0,0,0);clip:rect(0,0,0,0);white-space:nowrap;border-width:0;}/*!sc*/ data-styled.g5[id="_VisuallyHidden__VisuallyHidden-sc-11jhm7a-0"]{content:"brGdpi,"}/*!sc*/ .hWlpPn{position:relative;display:inline-block;}/*!sc*/ .hWlpPn::after{position:absolute;z-index:1000000;display:none;padding:0.5em 0.75em;font:normal normal 11px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";-webkit-font-smoothing:subpixel-antialiased;color:var(--tooltip-fgColor,var(--fgColor-onEmphasis,var(--color-fg-on-emphasis,#ffffff)));text-align:center;-webkit-text-decoration:none;text-decoration:none;text-shadow:none;text-transform:none;-webkit-letter-spacing:normal;-moz-letter-spacing:normal;-ms-letter-spacing:normal;letter-spacing:normal;word-wrap:break-word;white-space:pre;pointer-events:none;content:attr(aria-label);background:var(--tooltip-bgColor,var(--bgColor-emphasis,var(--color-neutral-emphasis-plus,#24292f)));border-radius:6px;opacity:0;}/*!sc*/ @-webkit-keyframes tooltip-appear{from{opacity:0;}to{opacity:1;}}/*!sc*/ @keyframes tooltip-appear{from{opacity:0;}to{opacity:1;}}/*!sc*/ .hWlpPn:hover::after,.hWlpPn:active::after,.hWlpPn:focus::after,.hWlpPn:focus-within::after{display:inline-block;-webkit-text-decoration:none;text-decoration:none;-webkit-animation-name:tooltip-appear;animation-name:tooltip-appear;-webkit-animation-duration:0.1s;animation-duration:0.1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;-webkit-animation-delay:0s;animation-delay:0s;}/*!sc*/ .hWlpPn.tooltipped-no-delay:hover::after,.hWlpPn.tooltipped-no-delay:active::after,.hWlpPn.tooltipped-no-delay:focus::after,.hWlpPn.tooltipped-no-delay:focus-within::after{-webkit-animation-delay:0s;animation-delay:0s;}/*!sc*/ .hWlpPn.tooltipped-multiline:hover::after,.hWlpPn.tooltipped-multiline:active::after,.hWlpPn.tooltipped-multiline:focus::after,.hWlpPn.tooltipped-multiline:focus-within::after{display:table-cell;}/*!sc*/ .hWlpPn.tooltipped-s::after,.hWlpPn.tooltipped-se::after,.hWlpPn.tooltipped-sw::after{top:100%;right:50%;margin-top:6px;}/*!sc*/ .hWlpPn.tooltipped-se::after{right:auto;left:50%;margin-left:-16px;}/*!sc*/ .hWlpPn.tooltipped-sw::after{margin-right:-16px;}/*!sc*/ .hWlpPn.tooltipped-n::after,.hWlpPn.tooltipped-ne::after,.hWlpPn.tooltipped-nw::after{right:50%;bottom:100%;margin-bottom:6px;}/*!sc*/ .hWlpPn.tooltipped-ne::after{right:auto;left:50%;margin-left:-16px;}/*!sc*/ .hWlpPn.tooltipped-nw::after{margin-right:-16px;}/*!sc*/ .hWlpPn.tooltipped-s::after,.hWlpPn.tooltipped-n::after{-webkit-transform:translateX(50%);-ms-transform:translateX(50%);transform:translateX(50%);}/*!sc*/ .hWlpPn.tooltipped-w::after{right:100%;bottom:50%;margin-right:6px;-webkit-transform:translateY(50%);-ms-transform:translateY(50%);transform:translateY(50%);}/*!sc*/ .hWlpPn.tooltipped-e::after{bottom:50%;left:100%;margin-left:6px;-webkit-transform:translateY(50%);-ms-transform:translateY(50%);transform:translateY(50%);}/*!sc*/ .hWlpPn.tooltipped-multiline::after{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:250px;word-wrap:break-word;white-space:pre-line;border-collapse:separate;}/*!sc*/ .hWlpPn.tooltipped-multiline.tooltipped-s::after,.hWlpPn.tooltipped-multiline.tooltipped-n::after{right:auto;left:50%;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);}/*!sc*/ .hWlpPn.tooltipped-multiline.tooltipped-w::after,.hWlpPn.tooltipped-multiline.tooltipped-e::after{right:100%;}/*!sc*/ .hWlpPn.tooltipped-align-right-2::after{right:0;margin-right:0;}/*!sc*/ .hWlpPn.tooltipped-align-left-2::after{left:0;margin-left:0;}/*!sc*/ data-styled.g16[id="Tooltip__TooltipBase-sc-17tf59c-0"]{content:"hWlpPn,"}/*!sc*/ .liVpTx{display:inline-block;overflow:hidden;text-overflow:ellipsis;vertical-align:top;white-space:nowrap;max-width:125px;}/*!sc*/ data-styled.g18[id="Truncate__StyledTruncate-sc-23o1d2-0"]{content:"liVpTx,"}/*!sc*/ </style> <!-- --> <!-- --> <div class="Box-sc-g0xbh4-0 iVEunk"><div class="Box-sc-g0xbh4-0 jzuOtQ"><div class="Box-sc-g0xbh4-0 bGojzy"></div></div><div class="Box-sc-g0xbh4-0 iNSVHo"><div class="Box-sc-g0xbh4-0 bVgnfw"><div class="Box-sc-g0xbh4-0 CEgMp"><button type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-label="master branch" data-testid="anchor-button" class="Box-sc-g0xbh4-0 gMOVLe prc-Button-ButtonBase-c50BI overview-ref-selector width-full" data-loading="false" data-size="medium" data-variant="default" aria-describedby="branch-picker-repos-header-ref-selector-loading-announcement" id="branch-picker-repos-header-ref-selector"><span data-component="buttonContent" class="Box-sc-g0xbh4-0 gUkoLg prc-Button-ButtonContent-HKbr-"><span data-component="text" class="prc-Button-Label-pTQ3x"><div class="Box-sc-g0xbh4-0 bZBlpz"><div class="Box-sc-g0xbh4-0 lhTYNA"><svg aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></div><div class="Box-sc-g0xbh4-0 ffLUq ref-selector-button-text-container"><span class="Box-sc-g0xbh4-0 bmcJak prc-Text-Text-0ima0"> <!-- -->master</span></div></div></span><span data-component="trailingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><button hidden="" data-hotkey-scope="read-only-cursor-text-area"></button></div><div class="Box-sc-g0xbh4-0 fLXEGX"><a style="--button-color:fg.muted" type="button" href="/tomitribe/crest/branches" class="Box-sc-g0xbh4-0 lmSMZJ prc-Button-ButtonBase-c50BI" data-loading="false" data-size="medium" data-variant="invisible" aria-describedby=":Rclab:-loading-announcement"><span data-component="buttonContent" class="Box-sc-g0xbh4-0 gUkoLg prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></span><span data-component="text" class="prc-Button-Label-pTQ3x">Branches</span></span></a><a style="--button-color:fg.muted" type="button" href="/tomitribe/crest/tags" class="Box-sc-g0xbh4-0 lmSMZJ prc-Button-ButtonBase-c50BI" data-loading="false" data-size="medium" data-variant="invisible" aria-describedby=":Rklab:-loading-announcement"><span data-component="buttonContent" class="Box-sc-g0xbh4-0 gUkoLg prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-tag" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path></svg></span><span data-component="text" class="prc-Button-Label-pTQ3x">Tags</span></span></a></div><div class="Box-sc-g0xbh4-0 dqfxud"><a style="--button-color:fg.muted" type="button" aria-label="Go to Branches page" href="/tomitribe/crest/branches" class="Box-sc-g0xbh4-0 fGwBZA prc-Button-ButtonBase-c50BI" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="invisible" aria-describedby=":Relab:-loading-announcement"><svg aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></a><a style="--button-color:fg.muted" type="button" aria-label="Go to Tags page" href="/tomitribe/crest/tags" class="Box-sc-g0xbh4-0 fGwBZA prc-Button-ButtonBase-c50BI" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="invisible" aria-describedby=":Rmlab:-loading-announcement"><svg aria-hidden="true" focusable="false" class="octicon octicon-tag" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path></svg></a></div></div><div class="Box-sc-g0xbh4-0 jxTzTd"><div class="Box-sc-g0xbh4-0 gqqBXN"><div class="Box-sc-g0xbh4-0 dzXgxt"><!--$--><div class="Box-sc-g0xbh4-0 iWFGlI"><span class="Box-sc-g0xbh4-0 vcvyP TextInput-wrapper prc-components-TextInputWrapper-i1ofR prc-components-TextInputBaseWrapper-ueK9q" data-leading-visual="true" data-trailing-visual="true" aria-busy="false"><span class="TextInput-icon" id=":R2j5ab:" aria-hidden="true"><svg aria-hidden="true" focusable="false" class="octicon octicon-search" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path></svg></span><input type="text" aria-label="Go to file" role="combobox" aria-controls="file-results-list" aria-expanded="false" aria-haspopup="dialog" autoCorrect="off" spellcheck="false" placeholder="Go to file" aria-describedby=":R2j5ab: :R2j5abH1:" data-component="input" class="prc-components-Input-Ic-y8" value=""/><span class="TextInput-icon" id=":R2j5abH1:" aria-hidden="true"></span></span></div><!--/$--></div><div class="Box-sc-g0xbh4-0 YUPas"><button type="button" class="prc-Button-ButtonBase-c50BI" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-describedby=":Rr5ab:-loading-announcement"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-HKbr-"><span data-component="text" class="prc-Button-Label-pTQ3x">Go to file</span></span></button></div><div class="react-directory-add-file-icon"></div><div class="react-directory-remove-file-icon"></div></div><button type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-c50BI" data-loading="false" data-size="medium" data-variant="primary" aria-describedby=":R55ab:-loading-announcement" id=":R55ab:"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-code hide-sm" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path></svg></span><span data-component="text" class="prc-Button-Label-pTQ3x">Code</span><span data-component="trailingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><div class="Box-sc-g0xbh4-0 izFOf"><button data-component="IconButton" type="button" aria-label="Open more actions menu" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-c50BI prc-Button-IconButton-szpyj" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-describedby=":R75ab:-loading-announcement" id=":R75ab:"><svg aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button></div></div></div><div class="Box-sc-g0xbh4-0 vIPPs"><div data-hpc="true"><button hidden="" data-testid="focus-next-element-button" data-hotkey="j"></button><button hidden="" data-testid="focus-previous-element-button" data-hotkey="k"></button><h2 class="sr-only ScreenReaderHeading-module__userSelectNone--vW4Cq prc-Heading-Heading-6CmGO" data-testid="screen-reader-heading" id="folders-and-files">Folders and files</h2><table aria-labelledby="folders-and-files" class="Box-sc-g0xbh4-0 fdROMU"><thead class="Box-sc-g0xbh4-0 jGKpsv"><tr class="Box-sc-g0xbh4-0 jdgHnn"><th colSpan="2" class="Box-sc-g0xbh4-0 bQivRW"><span class="text-bold">Name</span></th><th colSpan="1" class="Box-sc-g0xbh4-0 ldkMIO"><span class="text-bold">Name</span></th><th class="hide-sm"><div title="Last commit message" class="Truncate__StyledTruncate-sc-23o1d2-0 liVpTx width-fit"><span class="text-bold">Last commit message</span></div></th><th colSpan="1" class="Box-sc-g0xbh4-0 jMbWeI"><div title="Last commit date" class="Truncate__StyledTruncate-sc-23o1d2-0 liVpTx width-fit"><span class="text-bold">Last commit date</span></div></th></tr></thead><tbody><tr class="Box-sc-g0xbh4-0 gpqjiB"><td colSpan="3" class="bgColor-muted p-1 rounded-top-2"><div class="Box-sc-g0xbh4-0 dzCJzi"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone--vW4Cq prc-Heading-Heading-6CmGO" data-testid="screen-reader-heading">Latest commit</h2><div style="width:120px" class="Skeleton Skeleton--text" data-testid="loading"> </div><div class="d-flex flex-shrink-0 gap-2"><div data-testid="latest-commit-details" class="d-none d-sm-flex flex-items-center"></div><div class="d-flex gap-2"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone--vW4Cq prc-Heading-Heading-6CmGO" data-testid="screen-reader-heading">History</h2><a href="/tomitribe/crest/commits/master/" class="prc-Button-ButtonBase-c50BI d-none d-lg-flex LinkButton-module__code-view-link-button--xvCGA flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible" aria-describedby=":Raqj8pab:-loading-announcement"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span><span data-component="text" class="prc-Button-Label-pTQ3x"><span class="fgColor-default">496 Commits</span></span></span></a><div class="d-sm-none"></div><div class="d-flex d-lg-none"><span role="tooltip" aria-label="496 Commits" id="history-icon-button-tooltip" class="Tooltip__TooltipBase-sc-17tf59c-0 hWlpPn tooltipped-n"><a href="/tomitribe/crest/commits/master/" class="prc-Button-ButtonBase-c50BI LinkButton-module__code-view-link-button--xvCGA flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible" aria-describedby=":R1iqj8pab:-loading-announcement history-icon-button-tooltip"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span></span></a></span></div></div></div></div></td></tr><tr class="react-directory-row undefined" id="folder-row-0"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="crest-maven-plugin" aria-label="crest-maven-plugin, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/crest-maven-plugin">crest-maven-plugin</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="crest-maven-plugin" aria-label="crest-maven-plugin, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/crest-maven-plugin">crest-maven-plugin</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-1"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-api" aria-label="tomitribe-crest-api, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-api">tomitribe-crest-api</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-api" aria-label="tomitribe-crest-api, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-api">tomitribe-crest-api</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-2"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-archetype" aria-label="tomitribe-crest-archetype, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-archetype">tomitribe-crest-archetype</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-archetype" aria-label="tomitribe-crest-archetype, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-archetype">tomitribe-crest-archetype</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-3"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-arthur-extension" aria-label="tomitribe-crest-arthur-extension, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-arthur-extension">tomitribe-crest-arthur-extension</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-arthur-extension" aria-label="tomitribe-crest-arthur-extension, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-arthur-extension">tomitribe-crest-arthur-extension</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-4"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-cli" aria-label="tomitribe-crest-cli, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-cli">tomitribe-crest-cli</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-cli" aria-label="tomitribe-crest-cli, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-cli">tomitribe-crest-cli</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-5"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-generator" aria-label="tomitribe-crest-generator, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-generator">tomitribe-crest-generator</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-generator" aria-label="tomitribe-crest-generator, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-generator">tomitribe-crest-generator</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-6"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-test" aria-label="tomitribe-crest-test, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-test">tomitribe-crest-test</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-test" aria-label="tomitribe-crest-test, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-test">tomitribe-crest-test</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-7"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-xbean" aria-label="tomitribe-crest-xbean, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-xbean">tomitribe-crest-xbean</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest-xbean" aria-label="tomitribe-crest-xbean, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest-xbean">tomitribe-crest-xbean</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-8"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest" aria-label="tomitribe-crest, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest">tomitribe-crest</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="tomitribe-crest" aria-label="tomitribe-crest, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/tomitribe-crest">tomitribe-crest</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-9"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="toolz" aria-label="toolz, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/toolz">toolz</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="toolz" aria-label="toolz, (Directory)" class="Link--primary" href="/tomitribe/crest/tree/master/toolz">toolz</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-10"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".drone.yml" aria-label=".drone.yml, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/.drone.yml">.drone.yml</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".drone.yml" aria-label=".drone.yml, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/.drone.yml">.drone.yml</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-11"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".gitignore" aria-label=".gitignore, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/.gitignore">.gitignore</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".gitignore" aria-label=".gitignore, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/.gitignore">.gitignore</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-12"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".travis.yml" aria-label=".travis.yml, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/.travis.yml">.travis.yml</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".travis.yml" aria-label=".travis.yml, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/.travis.yml">.travis.yml</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-13"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="LICENSE" aria-label="LICENSE, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/LICENSE">LICENSE</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="LICENSE" aria-label="LICENSE, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/LICENSE">LICENSE</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-14"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="README.adoc" aria-label="README.adoc, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/README.adoc">README.adoc</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="README.adoc" aria-label="README.adoc, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/README.adoc">README.adoc</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-15"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="pom.xml" aria-label="pom.xml, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/pom.xml">pom.xml</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="pom.xml" aria-label="pom.xml, (File)" class="Link--primary" href="/tomitribe/crest/blob/master/pom.xml">pom.xml</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="Box-sc-g0xbh4-0 eNCcrz show-for-mobile" data-testid="view-all-files-row"><td colSpan="3" class="Box-sc-g0xbh4-0 bHTcCe"><div><button class="prc-Link-Link-85e08">View all files</button></div></td></tr></tbody></table></div><div class="Box-sc-g0xbh4-0 csrIcr"><div class="Box-sc-g0xbh4-0 bUQNHB"><div itemscope="" itemType="https://schema.org/abstract" class="Box-sc-g0xbh4-0 jPdcfu"><h2 class="_VisuallyHidden__VisuallyHidden-sc-11jhm7a-0 brGdpi">Repository files navigation</h2><nav class="Box-sc-g0xbh4-0 iphEWz prc-components-UnderlineWrapper-oOh5J" aria-label="Repository files"><ul class="prc-components-UnderlineItemList-b23Hf" role="list"><li class="Box-sc-g0xbh4-0 hUCRAk"><a class="prc-components-UnderlineItem-lJsg-" href="#" aria-current="page"><span data-component="icon"><svg aria-hidden="true" focusable="false" class="octicon octicon-book" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path></svg></span><span data-component="text" data-content="README">README</span></a></li><li class="Box-sc-g0xbh4-0 hUCRAk"><a class="prc-components-UnderlineItem-lJsg-" href="#"><span data-component="icon"><svg aria-hidden="true" focusable="false" class="octicon octicon-law" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8.75.75V2h.985c.304 0 .603.08.867.231l1.29.736c.038.022.08.033.124.033h2.234a.75.75 0 0 1 0 1.5h-.427l2.111 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.006.005-.01.01-.045.04c-.21.176-.441.327-.686.45C14.556 10.78 13.88 11 13 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L12.178 4.5h-.162c-.305 0-.604-.079-.868-.231l-1.29-.736a.245.245 0 0 0-.124-.033H8.75V13h2.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.5V3.5h-.984a.245.245 0 0 0-.124.033l-1.289.737c-.265.15-.564.23-.869.23h-.162l2.112 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.016.015-.045.04c-.21.176-.441.327-.686.45C4.556 10.78 3.88 11 3 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L2.178 4.5H1.75a.75.75 0 0 1 0-1.5h2.234a.249.249 0 0 0 .125-.033l1.288-.737c.265-.15.564-.23.869-.23h.984V.75a.75.75 0 0 1 1.5 0Zm2.945 8.477c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327Zm-10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327Z"></path></svg></span><span data-component="text" data-content="Apache-2.0 license">Apache-2.0 license</span></a></li></ul></nav><button style="--button-color:fg.subtle" type="button" aria-label="Outline" aria-haspopup="true" aria-expanded="false" tabindex="0" class="Box-sc-g0xbh4-0 cwoBXV prc-Button-ButtonBase-c50BI" data-loading="false" data-size="medium" data-variant="invisible" aria-describedby=":Rr9ab:-loading-announcement" id=":Rr9ab:"><svg aria-hidden="true" focusable="false" class="octicon octicon-list-unordered" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M5.75 2.5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5ZM2 14a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-6a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM2 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg></button></div><div class="Box-sc-g0xbh4-0 QkQOb js-snippet-clipboard-copy-unpositioned undefined" data-hpc="true"><article class="markdown-body entry-content container-lg" itemprop="text"><div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">CREST</h1><a id="user-content-crest" class="anchor" aria-label="Permalink: CREST" href="#crest"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div id="user-content-preamble" dir="auto"> <div dir="auto"> <div dir="auto"> <p dir="auto">Command-line API styled after JAX-RS</p> </div> <div dir="auto"> <p dir="auto">CREST allows you to get to the real work as quickly as possible when writing command line tools in Java.</p> </div> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto">100% annotation based</p> </li> <li> <p dir="auto">Use Bean Validation or custom validators on use input</p> </li> <li> <p dir="auto">Contains Several builtin validations</p> </li> <li> <p dir="auto">Generates help from annotations</p> </li> <li> <p dir="auto">Supports default values</p> </li> <li> <p dir="auto">Use variable substitution on defaults</p> </li> <li> <p dir="auto">Supports lists and var-ags</p> </li> <li> <p dir="auto">Supports any java type, usually out of the box</p> </li> </ul> </div> <div dir="auto"> <p dir="auto">Simply annotate the parameters of any Java method so it can be invoked from a command-line interface with near-zero additional work. Command-registration, help text and validation is taken care of for you.</p> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-start-your-project" tabindex="-1" class="heading-element" dir="auto">Start your project</h2><a id="user-content-start-your-project" class="anchor" aria-label="Permalink: Start your project" href="#start-your-project"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">Use the Maven archetype and run your first command now. Copy the following commands and paste them into your terminal.</p> </div> <div dir="auto"> <div dir="auto"> <pre>mvn archetype:generate -DarchetypeGroupId=org.tomitribe -DarchetypeArtifactId=tomitribe-crest-archetype -DarchetypeVersion=0.22 -DgroupId=org.example -DartifactId=mycommand cd mycommand/ mvn clean install ./target/mycommand hello</pre> </div> </div> <div dir="auto"> <p dir="auto">If all went well you should see the following output:</p> </div> <div dir="auto"> <div dir="auto"> <pre>Hello, World!</pre> </div> </div> <div dir="auto"> <p dir="auto">Yes, you can actually create executable command-line programs in Java!</p> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-example-rsync-as-a-crest-command" tabindex="-1" class="heading-element" dir="auto">Example: rsync as a Crest command</h2><a id="user-content-example-rsync-as-a-crest-command" class="anchor" aria-label="Permalink: Example: rsync as a Crest command" href="#example-rsync-as-a-crest-command"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">For example, to do something that might be similar to rsync in java, you could create the following method signature in any java object.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="package org.example.toolz; import org.tomitribe.crest.api.Command; import org.tomitribe.crest.api.Default; import org.tomitribe.crest.api.Option; import java.io.File; import java.net.URI; import java.util.regex.Pattern; public class AnyName { @Command public void rsync(@Option("recursive") boolean recursive, @Option("links") boolean links, @Option("perms") boolean perms, @Option("owner") boolean owner, @Option("group") boolean group, @Option("devices") boolean devices, @Option("specials") boolean specials, @Option("times") boolean times, @Option("exclude") Pattern exclude, @Option("exclude-from") File excludeFrom, @Option("include") Pattern include, @Option("include-from") File includeFrom, @Option("progress") @Default("true") boolean progress, URI[] sources, URI dest) { // TODO write the implementation... } }"><pre><span class="pl-k">package</span> <span class="pl-s1">org</span>.<span class="pl-s1">example</span>.<span class="pl-s1">toolz</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">api</span>.<span class="pl-s1">Command</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">api</span>.<span class="pl-s1">Default</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">api</span>.<span class="pl-s1">Option</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">io</span>.<span class="pl-s1">File</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">net</span>.<span class="pl-c1">URI</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">regex</span>.<span class="pl-s1">Pattern</span>; <span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">AnyName</span> { <span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">rsync</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"recursive"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">recursive</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"links"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">links</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"perms"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">perms</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"owner"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">owner</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"group"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">group</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"devices"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">devices</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"specials"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">specials</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"times"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">times</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"exclude"</span>) <span class="pl-smi">Pattern</span> <span class="pl-s1">exclude</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"exclude-from"</span>) <span class="pl-smi">File</span> <span class="pl-s1">excludeFrom</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"include"</span>) <span class="pl-smi">Pattern</span> <span class="pl-s1">include</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"include-from"</span>) <span class="pl-smi">File</span> <span class="pl-s1">includeFrom</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"progress"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"true"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">progress</span>, <span class="pl-smi">URI</span>[] <span class="pl-s1">sources</span>, <span class="pl-smi">URI</span> <span class="pl-s1">dest</span>) { <span class="pl-c">// TODO write the implementation...</span> } }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">Some quick notes on <code>@Command</code> usage:</p> </div> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto">Multiple classes that use <code>@Command</code> are allowed</p> </li> <li> <p dir="auto">Muttiple <code>@Command</code> methods are allowed in a class</p> </li> <li> <p dir="auto"><code>@Command</code> methods in a class may have the same or different name</p> </li> <li> <p dir="auto">The command name is derived from the method name if not specified in <code>@Command</code></p> </li> </ul> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h3 id="user-content-executing-the-command" tabindex="-1" class="heading-element" dir="auto">Executing the Command</h3><a id="user-content-executing-the-command" class="anchor" aria-label="Permalink: Executing the Command" href="#executing-the-command"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">Pack this class in an uber jar with the Crest library and you could execute this command from the command line as follows:</p> </div> <div dir="auto"> <div dir="auto"> <pre>$ java -jar target/toolz-1.0.0-SNAPSHOT.jar rsync Missing argument: URI... Usage: rsync [options] URI... URI Options: --devices --exclude=<Pattern> --exclude-from=<File> --group --include=<Pattern> --include-from=<File> --links --owner --perms --no-progress --recursive --specials --times</pre> </div> </div> <div dir="auto"> <p dir="auto">Of course, if we execute the command without the required arguments it will error out. This is the value of Crest — it does this dance for you.</p> </div> <div dir="auto"> <p dir="auto">In a dozen and more years of writing tools on different teams, two truths seem to prevail:</p> </div> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto">90% of writing scripts is parsing and validating user input</p> </li> <li> <p dir="auto">Don’t do that well and you’ll be lucky if it gets more than six months of use</p> </li> </ul> </div> <div dir="auto"> <p dir="auto">Computers are easy, humans are complex. Let Crest deal with the humans, you just write code.</p> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-help-text" tabindex="-1" class="heading-element" dir="auto">Help Text</h2><a id="user-content-help-text" class="anchor" aria-label="Permalink: Help Text" href="#help-text"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">In the above example we have no details in our help other than what can be generated from inspecting the code. To add actual descriptions to our code we simply need to put an <code>OptionDescriptions.properties</code> in the same package as our class.</p> </div> <div dir="auto"> <div dir="auto"> <pre>#code # <option> = <description> # <command>.<option> = <description> # The most specific key always wins recursive = recurse into directories links = copy symlinks as symlinks perms = preserve permissions owner = preserve owner (super-user only) group = preserve group times = preserve times devices = preserve device files (super-user only) specials = preserve special files exclude = exclude files matching PATTERN exclude-from = read exclude patterns from FILE include = don't exclude files matching PATTERN include-from = read include patterns from FILE progress = this is not the description that will be chosen rsync.progress = don't show progress during transfer</pre> </div> </div> <div dir="auto"> <p dir="auto">Some quick notes on <code>OptionDescription.properties</code> files:</p> </div> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto">These are Java <code>java.util.ResourceBundle</code> objects, so i18n is supported</p> </li> <li> <p dir="auto">Use <code>OptionDescription_en.properties</code> and similar for Locale specific help text</p> </li> <li> <p dir="auto">In DRY spirit, every <code>@Command</code> in the package shares the same <code>OptionDescription</code> ResourceBundle and keys</p> </li> <li> <p dir="auto">Use <code><command>.<option></code> as the key for situations where sharing is not desired</p> </li> </ul> </div> <div dir="auto"> <p dir="auto">With the above in our classpath, our command’s help will now look like the following:</p> </div> <div dir="auto"> <div dir="auto"> <pre>$ java -jar target/toolz-1.0.0-SNAPSHOT.jar rsync Missing argument: URI... Usage: rsync [options] URI... URI Options: --devices preserve device files (super-user only) --exclude=<Pattern> exclude files matching PATTERN --exclude-from=<File> read exclude patterns from FILE --group preserve group --include=<Pattern> don't exclude files matching PATTERN --include-from=<File> read include patterns from FILE --links copy symlinks as symlinks --owner preserve owner (super-user only) --perms preserve permissions --no-progress don't show progress during transfer --recursive recurse into directories --specials preserve special files --times preserve times</pre> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-bash-completion" tabindex="-1" class="heading-element" dir="auto">Bash Completion</h2><a id="user-content-bash-completion" class="anchor" aria-label="Permalink: Bash Completion" href="#bash-completion"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">For those that use Bash as their shell, command completion can be added to your environment as follows:</p> </div> <div dir="auto"> <div dir="auto"> <pre>source $(yourcommand _completion -f)</pre> </div> </div> <div dir="auto"> <p dir="auto">Using the example "toolz" cli created above, this could work like so:</p> </div> <div dir="auto"> <div dir="auto"> <pre>source $(java -jar target/toolz-1.0.0-SNAPSHOT.jar _completion -f)</pre> </div> </div> <div dir="auto"> <p dir="auto">If the toolz-1.0.0-SNAPSHOT.jar was turned into an executable file called <code>toolz</code> via the <code>really-executable-jar</code> maven plugin and <code>toolz</code> is in the system <code>PATH</code>, this would also work:</p> </div> <div dir="auto"> <div dir="auto"> <pre>source $(toolz _completion -f)</pre> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-default-values" tabindex="-1" class="heading-element" dir="auto">@Default values</h2><a id="user-content-default-values" class="anchor" aria-label="Permalink: @Default values" href="#default-values"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">Setting defaults to the <code>@Option</code> parameters of our <code>@Command</code> method can be done via the <code>@Default</code> annotation. Using as simplified version of our <code>rsync</code> example, we might possibly wish to specify a default <code>exclude</code> pattern.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public void rsync(@Option("exclude") @Default(".*~") Pattern exclude, @Option("include") Pattern include, @Option("progress") @Default("true") boolean progress, URI[] sources, URI dest) { // TODO write the implementation... }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">rsync</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"exclude"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">".*~"</span>) <span class="pl-smi">Pattern</span> <span class="pl-s1">exclude</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"include"</span>) <span class="pl-smi">Pattern</span> <span class="pl-s1">include</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"progress"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"true"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">progress</span>, <span class="pl-smi">URI</span>[] <span class="pl-s1">sources</span>, <span class="pl-smi">URI</span> <span class="pl-s1">dest</span>) { <span class="pl-c">// TODO write the implementation...</span> }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">Some quick notes about <code>@Option</code>:</p> </div> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto"><code>@Option</code> parameters are, by default, optional</p> </li> <li> <p dir="auto">When <code>@Default</code> is not used, the value will be its equivalent JVM default — typically <code>0</code> or <code>null</code></p> </li> <li> <p dir="auto">Add <code>@Required</code> to force a user to specify a value</p> </li> </ul> </div> <div dir="auto"> <p dir="auto">Default values will show up in help output automatically, no need to update your <code>OptionDescriptions.properties</code></p> </div> <div dir="auto"> <div dir="auto"> <pre>Usage: rsync [options] URI... URI Options: --exclude=<Pattern> exclude files matching PATTERN (default: .*~) --include=<Pattern> don't exclude files matching PATTERN --no-progress don't show progress during transfer</pre> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h3 id="user-content-advanced" tabindex="-1" class="heading-element" dir="auto">Advanced</h3><a id="user-content-advanced" class="anchor" aria-label="Permalink: Advanced" href="#advanced"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">Default values also support interpolations:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public void myCommand(@Option("myoption") @Default("${env.MY_ENV_VAR}") String exclude) { // TODO write the implementation... } @Command public void myCommand(@Option("myoption") @Default("${sys.MY_ENV_VAR}") String exclude) { // TODO write the implementation... }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">myCommand</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"myoption"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"${env.MY_ENV_VAR}"</span>) <span class="pl-smi">String</span> <span class="pl-s1">exclude</span>) { <span class="pl-c">// TODO write the implementation...</span> } <span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">myCommand</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"myoption"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"${sys.MY_ENV_VAR}"</span>) <span class="pl-smi">String</span> <span class="pl-s1">exclude</span>) { <span class="pl-c">// TODO write the implementation...</span> }</pre></div> </div> </div> <div dir="auto"> <p dir="auto"><code>env</code> is a prefix used to read the default in the environment variables and <code>sys</code> to read the system properties.</p> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Tip</div> </td> <td> you can also register custom <code>DefaultsContext</code> in the interpolation registry using <code>META-INF/services/org.tomitribe.crest.contexts.DefaultsContext</code> file to register it (just put a fully qualified implementation per line). The prefix will be the simple name of the implementation in lowercase. For instance <code>org.company.MyEnv</code> will use <code>myenv</code>. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> <div dir="auto"> <p dir="auto">Finally the interpolation in such a form supports defaults:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public void myCommand(@Option("myoption") @Default("${env.MY_ENV_VAR:defaultIfEnvNotSet}") String exclude) { // TODO write the implementation... }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">myCommand</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"myoption"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"${env.MY_ENV_VAR:defaultIfEnvNotSet}"</span>) <span class="pl-smi">String</span> <span class="pl-s1">exclude</span>) { <span class="pl-c">// TODO write the implementation...</span> }</pre></div> </div> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-option-lists-and-arrays" tabindex="-1" class="heading-element" dir="auto">@Option Lists and Arrays</h2><a id="user-content-option-lists-and-arrays" class="anchor" aria-label="Permalink: @Option Lists and Arrays" href="#option-lists-and-arrays"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">There are situations where you might want to allow the same flag to be specified twice. Simply turn the <code>@Option</code> parameter into an array or list that uses generics.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public void rsync(@Option("exclude") @Default(".*~") Pattern[] excludes, @Option("include") Pattern include, @Option("progress") @Default("true") boolean progress, URI[] sources, URI dest) { // TODO write the implementation... }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">rsync</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"exclude"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">".*~"</span>) <span class="pl-smi">Pattern</span>[] <span class="pl-s1">excludes</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"include"</span>) <span class="pl-smi">Pattern</span> <span class="pl-s1">include</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"progress"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"true"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">progress</span>, <span class="pl-smi">URI</span>[] <span class="pl-s1">sources</span>, <span class="pl-smi">URI</span> <span class="pl-s1">dest</span>) { <span class="pl-c">// TODO write the implementation...</span> }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">The user can now specify multiple values when invoking the command by repeating the flag.</p> </div> <div dir="auto"> <div dir="auto"> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$ java -jar target/toolz-1.0.0-SNAPSHOT.jar rsync --exclude=".*\.log" --exclude=".*\.iml" ..."><pre class="notranslate"><code>$ java -jar target/toolz-1.0.0-SNAPSHOT.jar rsync --exclude=".*\.log" --exclude=".*\.iml" ...</code></pre></div> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-default-option-lists-and-arrays" tabindex="-1" class="heading-element" dir="auto">@Default @Option Lists and Arrays</h2><a id="user-content-default-option-lists-and-arrays" class="anchor" aria-label="Permalink: @Default @Option Lists and Arrays" href="#default-option-lists-and-arrays"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">Should you want to specify these two <code>exclude</code> values as the defaults, simply use a <strong>comma</strong> <code>,</code> to separate them in <code>@Default</code></p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public void rsync(@Option("exclude") @Default(".*\\.iml,.*\\.iml") Pattern[] excludes, @Option("include") Pattern include, @Option("progress") @Default("true") boolean progress, URI[] sources, URI dest) { }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">rsync</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"exclude"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">".*<span class="pl-cce">\\</span>.iml,.*<span class="pl-cce">\\</span>.iml"</span>) <span class="pl-smi">Pattern</span>[] <span class="pl-s1">excludes</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"include"</span>) <span class="pl-smi">Pattern</span> <span class="pl-s1">include</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"progress"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"true"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">progress</span>, <span class="pl-smi">URI</span>[] <span class="pl-s1">sources</span>, <span class="pl-smi">URI</span> <span class="pl-s1">dest</span>) { }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">If you happen to need comma for something, use <strong>tab</strong> <code>\t</code> instead. When a tab is present in the <code>@Default</code> string, it becomes the preferred splitter.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public void rsync(@Option("exclude") @Default(".*\\.iml\t.*\\.iml") Pattern[] excludes, @Option("include") Pattern include, @Option("progress") @Default("true") boolean progress, URI[] sources, URI dest) { }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">rsync</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"exclude"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">".*<span class="pl-cce">\\</span>.iml<span class="pl-cce">\t</span>.*<span class="pl-cce">\\</span>.iml"</span>) <span class="pl-smi">Pattern</span>[] <span class="pl-s1">excludes</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"include"</span>) <span class="pl-smi">Pattern</span> <span class="pl-s1">include</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"progress"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"true"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">progress</span>, <span class="pl-smi">URI</span>[] <span class="pl-s1">sources</span>, <span class="pl-smi">URI</span> <span class="pl-s1">dest</span>) { }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">If you happen to need both tab and comma for something (really????), use <strong>unicode</strong> zero <code>\u0000</code> instead.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public void rsync(@Option("exclude") @Default(".*\\.iml\u0000.*\\.iml") Pattern[] excludes, @Option("include") Pattern include, @Option("progress") @Default("true") boolean progress, URI[] sources, URI dest) { }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">rsync</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"exclude"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">".*<span class="pl-cce">\\</span>.iml<span class="pl-cce">\u0000</span>.*<span class="pl-cce">\\</span>.iml"</span>) <span class="pl-smi">Pattern</span>[] <span class="pl-s1">excludes</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"include"</span>) <span class="pl-smi">Pattern</span> <span class="pl-s1">include</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"progress"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"true"</span>) <span class="pl-smi">boolean</span> <span class="pl-s1">progress</span>, <span class="pl-smi">URI</span>[] <span class="pl-s1">sources</span>, <span class="pl-smi">URI</span> <span class="pl-s1">dest</span>) { }</pre></div> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-default-and-variable-substitution" tabindex="-1" class="heading-element" dir="auto">@Default and ${variable} Substitution</h2><a id="user-content-default-and-variable-substitution" class="anchor" aria-label="Permalink: @Default and ${variable} Substitution" href="#default-and-variable-substitution"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">In the event you want to make defaults contextual, you can use <code>${some.property}</code> in the <code>@Default</code> string and the <code>java.lang.System.getProperties()</code> object to supply the value.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public void hello(@Option("name") @Default("${user.name}") String user) throws Exception System.out.printf("Hello, %s%n", user); }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">hello</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"name"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"${user.name}"</span>) <span class="pl-smi">String</span> <span class="pl-s1">user</span>) <span class="pl-k">throws</span> <span class="pl-smi">Exception</span> <span class="pl-smi">System</span>.<span class="pl-s1">out</span>.<span class="pl-en">printf</span>(<span class="pl-s">"Hello, %s%n"</span>, <span class="pl-s1">user</span>); }</pre></div> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-return-values" tabindex="-1" class="heading-element" dir="auto">Return Values</h2><a id="user-content-return-values" class="anchor" aria-label="Permalink: Return Values" href="#return-values"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">In the above we wrote to the console, which is fine for simple things but can make testing hard. So far our commands are still POJOs and nothing is stopping us from unit testing them as plain java objects — except asserting output writen to <code>System.out</code>.</p> </div> <div dir="auto"> <p dir="auto">Simply return <code>java.lang.String</code> and it will be written to <code>System.out</code> for you.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public String hello(@Option("name") @Default("${user.name}") String user) throws Exception return String.format("Hello, %s%n", user); }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">String</span> <span class="pl-en">hello</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"name"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"${user.name}"</span>) <span class="pl-smi">String</span> <span class="pl-s1">user</span>) <span class="pl-k">throws</span> <span class="pl-smi">Exception</span> <span class="pl-k">return</span> <span class="pl-smi">String</span>.<span class="pl-en">format</span>(<span class="pl-s">"Hello, %s%n"</span>, <span class="pl-s1">user</span>); }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">In the event you need to write a significant amount of data, you can return <code>org.tomitribe.crest.api.StreamingOutput</code> which is an exact copy of the equivalent JAX-RS <a href="http://docs.oracle.com/javaee/6/api/javax/ws/rs/core/StreamingOutput.html" rel="nofollow">StreamingOutput</a> interface.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public StreamingOutput cat(final File file) { if (!file.exists()) throw new IllegalStateException("File does not exist: " + file.getAbsolutePath()); if (!file.canRead()) throw new IllegalStateException("Not readable: " + file.getAbsolutePath()); if (!file.isFile()) throw new IllegalStateException("Not a file: " + file.getAbsolutePath()); return new StreamingOutput() { @Override public void write(OutputStream output) throws IOException { final InputStream input = new BufferedInputStream(new FileInputStream(file)); try { final byte[] buffer = new byte[1024]; int length; while ((length = input.read(buffer)) != -1) { output.write(buffer, 0, length); } output.flush(); } finally { if (input != null) input.close(); } } }; }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">StreamingOutput</span> <span class="pl-en">cat</span>(<span class="pl-k">final</span> <span class="pl-smi">File</span> <span class="pl-s1">file</span>) { <span class="pl-k">if</span> (!<span class="pl-s1">file</span>.<span class="pl-en">exists</span>()) <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">IllegalStateException</span>(<span class="pl-s">"File does not exist: "</span> + <span class="pl-s1">file</span>.<span class="pl-en">getAbsolutePath</span>()); <span class="pl-k">if</span> (!<span class="pl-s1">file</span>.<span class="pl-en">canRead</span>()) <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">IllegalStateException</span>(<span class="pl-s">"Not readable: "</span> + <span class="pl-s1">file</span>.<span class="pl-en">getAbsolutePath</span>()); <span class="pl-k">if</span> (!<span class="pl-s1">file</span>.<span class="pl-en">isFile</span>()) <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">IllegalStateException</span>(<span class="pl-s">"Not a file: "</span> + <span class="pl-s1">file</span>.<span class="pl-en">getAbsolutePath</span>()); <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">StreamingOutput</span>() { <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">write</span>(<span class="pl-smi">OutputStream</span> <span class="pl-s1">output</span>) <span class="pl-k">throws</span> <span class="pl-smi">IOException</span> { <span class="pl-k">final</span> <span class="pl-smi">InputStream</span> <span class="pl-s1">input</span> = <span class="pl-k">new</span> <span class="pl-smi">BufferedInputStream</span>(<span class="pl-k">new</span> <span class="pl-smi">FileInputStream</span>(<span class="pl-s1">file</span>)); <span class="pl-k">try</span> { <span class="pl-k">final</span> <span class="pl-smi">byte</span>[] <span class="pl-s1">buffer</span> = <span class="pl-k">new</span> <span class="pl-smi">byte</span>[<span class="pl-c1">1024</span>]; <span class="pl-smi">int</span> <span class="pl-s1">length</span>; <span class="pl-k">while</span> ((<span class="pl-s1">length</span> = <span class="pl-s1">input</span>.<span class="pl-en">read</span>(<span class="pl-s1">buffer</span>)) != -<span class="pl-c1">1</span>) { <span class="pl-s1">output</span>.<span class="pl-en">write</span>(<span class="pl-s1">buffer</span>, <span class="pl-c1">0</span>, <span class="pl-s1">length</span>); } <span class="pl-s1">output</span>.<span class="pl-en">flush</span>(); } <span class="pl-k">finally</span> { <span class="pl-k">if</span> (<span class="pl-s1">input</span> != <span class="pl-c1">null</span>) <span class="pl-s1">input</span>.<span class="pl-en">close</span>(); } } }; }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">Note a <code>null</code> check is not necessary for the <code>File file</code> parameter as Crest will not let the value of any plain argument be unspecified. All parameters which do not use <code>@Option</code> are treated as required</p> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-stream-injections" tabindex="-1" class="heading-element" dir="auto">Stream injections</h2><a id="user-content-stream-injections" class="anchor" aria-label="Permalink: Stream injections" href="#stream-injections"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">Command are often linked to console I/O. For that reason it is important to be able to interact with Crest in/out/error streams. They are provided by the contextual <code>Environment</code> instance and using its thread local you can retrieve them. However to make it easier to work with you can inject them as well.</p> </div> <div dir="auto"> <p dir="auto">Out stream (out and error ones) needs to be <code>PrintStream</code> typed and input is typed as a <code>InputStream</code>. Just use these types as command parameters and decorate it with <code>@In</code>/<code>@Out</code>/<code>@Err</code>:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public class IOMe { @org.tomitribe.crest.api.Command public static void asserts(@In final InputStream in, @Out final PrintStream out, @Err PrintStream err) { // ... } }"><pre><span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">IOMe</span> { <span class="pl-c1">@</span><span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">api</span>.<span class="pl-s1">Command</span> <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-smi">void</span> <span class="pl-en">asserts</span>(<span class="pl-c1">@</span><span class="pl-c1">In</span> <span class="pl-k">final</span> <span class="pl-smi">InputStream</span> <span class="pl-s1">in</span>, <span class="pl-c1">@</span><span class="pl-c1">Out</span> <span class="pl-k">final</span> <span class="pl-smi">PrintStream</span> <span class="pl-s1">out</span>, <span class="pl-c1">@</span><span class="pl-c1">Err</span> <span class="pl-smi">PrintStream</span> <span class="pl-s1">err</span>) { <span class="pl-c">// ...</span> } }</pre></div> </div> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Note</div> </td> <td> using a parameter typed <code>Environment</code> you’ll get it injected as well but this one is not in <code>crest-api</code>. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-custom-java-types" tabindex="-1" class="heading-element" dir="auto">Custom Java Types</h2><a id="user-content-custom-java-types" class="anchor" aria-label="Permalink: Custom Java Types" href="#custom-java-types"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">You may have been seeing <code>File</code> and <code>Pattern</code> in the above examples and wondering exactly which Java classes Crest supports parameters to <code>@Command</code> methods. The short answer is, any. Crest does <strong>not</strong> use <code>java.beans.PropertyEditor</code> implementations by default like libraries such as Spring do.</p> </div> <div dir="auto"> <p dir="auto">After nearly 20 years of Java’s existence, it’s safe to say two styles dominate converting a <code>String</code> into a Java object:</p> </div> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto">A <strong>Constructor</strong> that take a single String as an argument. Examples:</p> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto"><code>java.io.File(String)</code></p> </li> <li> <p dir="auto"><code>java.lang.Integer(String)</code></p> </li> <li> <p dir="auto"><code>java.net.URL(String)</code></p> </li> </ul> </div> </li> <li> <p dir="auto">A <strong>static method</strong> that returns an instance of the same class. Examples:</p> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto"><code>java.util.regex.Pattern.compile(String)</code></p> </li> <li> <p dir="auto"><code>java.net.URI.create(String)</code></p> </li> <li> <p dir="auto"><code>java.util.concurrent.TimeUnit.valueOf(String)</code></p> </li> </ul> </div> </li> </ul> </div> <div dir="auto"> <p dir="auto">Use either of these conventions and Crest will have no problem instantiating your object with the user-supplied <code>String</code> from the command-line args.</p> </div> <div dir="auto"> <p dir="auto">This should cover <strong>95%</strong> of all cases, but in the event it does not, you can create a <code>java.beans.PropertyEditor</code> and register it with the JVM. Use your Google-fu to learn how to do that.</p> </div> <div dir="auto"> <p dir="auto">The order of precedence is as follows:</p> </div> <div dir="auto"> <ol dir="auto"> <li> <p dir="auto">Constructor</p> </li> <li> <p dir="auto">Static method</p> </li> <li> <p dir="auto"><code>java.beans.PropertyEditor</code></p> </li> </ol> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-custom-validation" tabindex="-1" class="heading-element" dir="auto">Custom Validation</h2><a id="user-content-custom-validation" class="anchor" aria-label="Permalink: Custom Validation" href="#custom-validation"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">If we look at our <code>cat</code> command we had earlier and yank the very boiler-plate read/write stream logic, all we have left is some code validating the user input.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public StreamingOutput cat(final File file) { if (!file.exists()) throw new IllegalStateException("File does not exist: " + file.getAbsolutePath()); if (!file.canRead()) throw new IllegalStateException("Not readable: " + file.getAbsolutePath()); if (!file.isFile()) throw new IllegalStateException("Not a file: " + file.getAbsolutePath()); return new StreamingOutput() { @Override public void write(OutputStream os) throws IOException { IO.copy(file, os); } }; }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">StreamingOutput</span> <span class="pl-en">cat</span>(<span class="pl-k">final</span> <span class="pl-smi">File</span> <span class="pl-s1">file</span>) { <span class="pl-k">if</span> (!<span class="pl-s1">file</span>.<span class="pl-en">exists</span>()) <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">IllegalStateException</span>(<span class="pl-s">"File does not exist: "</span> + <span class="pl-s1">file</span>.<span class="pl-en">getAbsolutePath</span>()); <span class="pl-k">if</span> (!<span class="pl-s1">file</span>.<span class="pl-en">canRead</span>()) <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">IllegalStateException</span>(<span class="pl-s">"Not readable: "</span> + <span class="pl-s1">file</span>.<span class="pl-en">getAbsolutePath</span>()); <span class="pl-k">if</span> (!<span class="pl-s1">file</span>.<span class="pl-en">isFile</span>()) <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">IllegalStateException</span>(<span class="pl-s">"Not a file: "</span> + <span class="pl-s1">file</span>.<span class="pl-en">getAbsolutePath</span>()); <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">StreamingOutput</span>() { <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">write</span>(<span class="pl-smi">OutputStream</span> <span class="pl-s1">os</span>) <span class="pl-k">throws</span> <span class="pl-smi">IOException</span> { <span class="pl-c1">IO</span>.<span class="pl-en">copy</span>(<span class="pl-s1">file</span>, <span class="pl-s1">os</span>); } }; }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">This validation code, too, can be yanked. Crest supports the use of <a href="http://beanvalidation.org" rel="nofollow">Bean Validation</a> to validate <code>@Command</code> method parameters.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public StreamingOutput cat(@Exists @Readable final File file) { if (!file.isFile()) throw new IllegalStateException("Not a file: " + file.getAbsolutePath()); return new StreamingOutput() { @Override public void write(OutputStream os) throws IOException { IO.copy(file, os); } }; }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">StreamingOutput</span> <span class="pl-en">cat</span>(<span class="pl-c1">@</span><span class="pl-c1">Exists</span> <span class="pl-c1">@</span><span class="pl-c1">Readable</span> <span class="pl-k">final</span> <span class="pl-smi">File</span> <span class="pl-s1">file</span>) { <span class="pl-k">if</span> (!<span class="pl-s1">file</span>.<span class="pl-en">isFile</span>()) <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">IllegalStateException</span>(<span class="pl-s">"Not a file: "</span> + <span class="pl-s1">file</span>.<span class="pl-en">getAbsolutePath</span>()); <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">StreamingOutput</span>() { <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">write</span>(<span class="pl-smi">OutputStream</span> <span class="pl-s1">os</span>) <span class="pl-k">throws</span> <span class="pl-smi">IOException</span> { <span class="pl-c1">IO</span>.<span class="pl-en">copy</span>(<span class="pl-s1">file</span>, <span class="pl-s1">os</span>); } }; }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">Here we’ve eliminated two of our very tedious checks with Bean Validation annotations that Crest provides out of the box, but we still have one more to get rid of. We can eliminate that one by writing our own annotation and using the Bean Validation API to wire it all together.</p> </div> <div dir="auto"> <p dir="auto">Here is what an annotation to do the <code>file.isFile()</code> check might look like — let’s call the annotation simply <code>@IsFile</code></p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="package org.example.toolz; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.Payload; import java.io.File; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import org.tomitribe.crest.val.Exists; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Exists @Documented @javax.validation.Constraint(validatedBy = {IsFile.Constraint.class}) @Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER}) @Retention(RUNTIME) public @interface IsFile { Class<?>[] groups() default {}; String message() default "{org.exampe.toolz.IsFile.message}"; Class<? extends Payload>[] payload() default {}; public static class Constraint implements ConstraintValidator<IsFile, File> { @Override public void initialize(IsFile constraintAnnotation) { } @Override public boolean isValid(File file, ConstraintValidatorContext context) { return file.isFile(); } } }"><pre><span class="pl-k">package</span> <span class="pl-s1">org</span>.<span class="pl-s1">example</span>.<span class="pl-s1">toolz</span>; <span class="pl-k">import</span> <span class="pl-s1">javax</span>.<span class="pl-s1">validation</span>.<span class="pl-s1">ConstraintValidator</span>; <span class="pl-k">import</span> <span class="pl-s1">javax</span>.<span class="pl-s1">validation</span>.<span class="pl-s1">ConstraintValidatorContext</span>; <span class="pl-k">import</span> <span class="pl-s1">javax</span>.<span class="pl-s1">validation</span>.<span class="pl-s1">Payload</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">io</span>.<span class="pl-s1">File</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">lang</span>.<span class="pl-s1">annotation</span>.<span class="pl-s1">Documented</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">lang</span>.<span class="pl-s1">annotation</span>.<span class="pl-s1">Retention</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">lang</span>.<span class="pl-s1">annotation</span>.<span class="pl-s1">Target</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">val</span>.<span class="pl-s1">Exists</span>; <span class="pl-k">import</span> <span class="pl-k">static</span> <span class="pl-s1">java</span>.<span class="pl-s1">lang</span>.<span class="pl-s1">annotation</span>.<span class="pl-s1">ElementType</span>.<span class="pl-c1">ANNOTATION_TYPE</span>; <span class="pl-k">import</span> <span class="pl-k">static</span> <span class="pl-s1">java</span>.<span class="pl-s1">lang</span>.<span class="pl-s1">annotation</span>.<span class="pl-s1">ElementType</span>.<span class="pl-c1">FIELD</span>; <span class="pl-k">import</span> <span class="pl-k">static</span> <span class="pl-s1">java</span>.<span class="pl-s1">lang</span>.<span class="pl-s1">annotation</span>.<span class="pl-s1">ElementType</span>.<span class="pl-c1">METHOD</span>; <span class="pl-k">import</span> <span class="pl-k">static</span> <span class="pl-s1">java</span>.<span class="pl-s1">lang</span>.<span class="pl-s1">annotation</span>.<span class="pl-s1">ElementType</span>.<span class="pl-c1">PARAMETER</span>; <span class="pl-k">import</span> <span class="pl-k">static</span> <span class="pl-s1">java</span>.<span class="pl-s1">lang</span>.<span class="pl-s1">annotation</span>.<span class="pl-s1">RetentionPolicy</span>.<span class="pl-c1">RUNTIME</span>; <span class="pl-c1">@</span><span class="pl-c1">Exists</span> <span class="pl-c1">@</span><span class="pl-c1">Documented</span> <span class="pl-c1">@</span><span class="pl-s1">javax</span>.<span class="pl-s1">validation</span>.<span class="pl-s1">Constraint</span>(<span class="pl-s1">validatedBy</span> = {<span class="pl-smi">IsFile</span>.<span class="pl-smi">Constraint</span>.<span class="pl-k">class</span>}) <span class="pl-c1">@</span><span class="pl-c1">Target</span>({<span class="pl-c1">METHOD</span>, <span class="pl-c1">FIELD</span>, <span class="pl-c1">ANNOTATION_TYPE</span>, <span class="pl-c1">PARAMETER</span>}) <span class="pl-c1">@</span><span class="pl-c1">Retention</span>(<span class="pl-c1">RUNTIME</span>) <span class="pl-k">public</span> @interface <span class="pl-s1">IsFile</span> { <span class="pl-smi">Class</span><?>[] <span class="pl-s1">groups</span>() <span class="pl-k">default</span> {}; <span class="pl-smi">String</span> <span class="pl-s1">message</span>() <span class="pl-k">default</span> <span class="pl-s">"{org.exampe.toolz.IsFile.message}"</span>; <span class="pl-smi">Class</span><? <span class="pl-k">extends</span> <span class="pl-smi">Payload</span>>[] <span class="pl-s1">payload</span>() <span class="pl-k">default</span> {}; <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-k">class</span> <span class="pl-smi">Constraint</span> <span class="pl-k">implements</span> <span class="pl-smi">ConstraintValidator</span><<span class="pl-smi">IsFile</span>, <span class="pl-smi">File</span>> { <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">initialize</span>(<span class="pl-smi">IsFile</span> <span class="pl-s1">constraintAnnotation</span>) { } <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">boolean</span> <span class="pl-en">isValid</span>(<span class="pl-smi">File</span> <span class="pl-s1">file</span>, <span class="pl-smi">ConstraintValidatorContext</span> <span class="pl-s1">context</span>) { <span class="pl-k">return</span> <span class="pl-s1">file</span>.<span class="pl-en">isFile</span>(); } } }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">We can then update our code as follows to use this validation and eliminate all our boiler-plate.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public StreamingOutput cat(@IsFile @Readable final File file) { return new StreamingOutput() { @Override public void write(OutputStream os) throws IOException { IO.copy(file, os); } }; }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">StreamingOutput</span> <span class="pl-en">cat</span>(<span class="pl-c1">@</span><span class="pl-c1">IsFile</span> <span class="pl-c1">@</span><span class="pl-c1">Readable</span> <span class="pl-k">final</span> <span class="pl-smi">File</span> <span class="pl-s1">file</span>) { <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">StreamingOutput</span>() { <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">write</span>(<span class="pl-smi">OutputStream</span> <span class="pl-s1">os</span>) <span class="pl-k">throws</span> <span class="pl-smi">IOException</span> { <span class="pl-c1">IO</span>.<span class="pl-en">copy</span>(<span class="pl-s1">file</span>, <span class="pl-s1">os</span>); } }; }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">Notice that we also removed <code>@Exists</code> from the method parameter? Since we put <code>@Exists</code> on the <code>@IsFile</code> annotation, the <code>@IsFile</code> annotation effectively inherits the <code>@Exists</code> logic. Our <code>@IsFile</code> annotation could inherit any number of annotations this way.</p> </div> <div dir="auto"> <p dir="auto">As the true strength of a great library of tools is the effort put into ensuring correct input, it’s very wise to bite the bullet and proactively invest in creating a reusable set of validation annotations to cover your typical input types.</p> </div> <div dir="auto"> <p dir="auto">Pull requests are <strong>very</strong> strongly encouraged for any annotations that might be useful to others.</p> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h3 id="user-content-bean-validation-less-validations" tabindex="-1" class="heading-element" dir="auto">Bean Validation-less validations</h3><a id="user-content-bean-validation-less-validations" class="anchor" aria-label="Permalink: Bean Validation-less validations" href="#bean-validation-less-validations"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">You can also use the built-in crest validator style, it enables to lighten the dependencies by not requiring bean validation. To do that you must:</p> </div> <div dir="auto"> <ol dir="auto"> <li> <p dir="auto">define a custom validation annotation</p> </li> <li> <p dir="auto">implementation the validation as a <code>Consumer<ParamType></code> or <code>BiConsumer<AnnotationDefinedIn1, ParamType></code></p> </li> </ol> </div> <div dir="auto"> <p dir="auto">If the validation fails, the implementation just throws an exception with a meaningful error message.</p> </div> <div dir="auto"> <p dir="auto">Here is a trivial example to check a <code>Path</code> is a directory:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content=" @Target(PARAMETER) @Retention(RUNTIME) @Validation(CrestDirectory.Impl.class) public @interface CrestDirectory { } public class Impl implements Consumer<Path> { @Override public void accept(final Path file) { if (!Files.isDirectory(file)) { throw new IllegalStateException("'" + file + "' is not a directory"); } } }"><pre> <span class="pl-c1">@</span><span class="pl-c1">Target</span>(<span class="pl-c1">PARAMETER</span>) <span class="pl-c1">@</span><span class="pl-c1">Retention</span>(<span class="pl-c1">RUNTIME</span>) <span class="pl-c1">@</span><span class="pl-c1">Validation</span>(<span class="pl-smi">CrestDirectory</span>.<span class="pl-smi">Impl</span>.<span class="pl-k">class</span>) <span class="pl-k">public</span> @interface <span class="pl-s1">CrestDirectory</span> { } <span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">Impl</span> <span class="pl-k">implements</span> <span class="pl-smi">Consumer</span><<span class="pl-smi">Path</span>> { <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">accept</span>(<span class="pl-k">final</span> <span class="pl-smi">Path</span> <span class="pl-s1">file</span>) { <span class="pl-k">if</span> (!<span class="pl-smi">Files</span>.<span class="pl-en">isDirectory</span>(<span class="pl-s1">file</span>)) { <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">IllegalStateException</span>(<span class="pl-s">"'"</span> + <span class="pl-s1">file</span> + <span class="pl-s">"' is not a directory"</span>); } } }</pre></div> </div> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Tip</div> </td> <td> the instances of the implementation are looked up by class in the <code>Environment</code> and if none matches a plain <code>new</code> is done calling the default constructor. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-maven-pom-xml-setup" tabindex="-1" class="heading-element" dir="auto">Maven pom.xml setup</h2><a id="user-content-maven-pomxml-setup" class="anchor" aria-label="Permalink: Maven pom.xml setup" href="#maven-pomxml-setup"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">The following sample pom.xml will get you 90% of your way to fun with Crest and project that will output a small uber jar with all the required dependencies.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-text-xml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>toolz</artifactId> <version>0.3-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.tomitribe</groupId> <artifactId>tomitribe-crest</artifactId> <version>0.3-SNAPSHOT</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <!-- Add tomitribe-crest-xbean if you want classpath scanning for @Command --> <dependency> <groupId>org.tomitribe</groupId> <artifactId>tomitribe-crest-xbean</artifactId> <version>0.3-SNAPSHOT</version> </dependency> </dependencies> <build> <defaultGoal>install</defaultGoal> <plugins> <plugin> <artifactId>maven-shade-plugin</artifactId> <version>2.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>org.tomitribe.crest.Main</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> <repositories> <repository> <id>sonatype-nexus-snapshots</id> <name>Sonatype Nexus Snapshots</name> <url>https://oss.sonatype.org/content/repositories/snapshots</url> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> </project>"><pre><?<span class="pl-ent">xml</span><span class="pl-e"> version</span>=<span class="pl-s"><span class="pl-pds">"</span>1.0<span class="pl-pds">"</span></span>?> <<span class="pl-ent">project</span> <span class="pl-e">xsi</span><span class="pl-e">:</span><span class="pl-e">schemaLocation</span>=<span class="pl-s"><span class="pl-pds">"</span>http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd<span class="pl-pds">"</span></span> <span class="pl-e">xmlns</span>=<span class="pl-s"><span class="pl-pds">"</span>http://maven.apache.org/POM/4.0.0<span class="pl-pds">"</span></span> <span class="pl-e">xmlns</span><span class="pl-e">:</span><span class="pl-e">xsi</span>=<span class="pl-s"><span class="pl-pds">"</span>http://www.w3.org/2001/XMLSchema-instance<span class="pl-pds">"</span></span>> <<span class="pl-ent">modelVersion</span>>4.0.0</<span class="pl-ent">modelVersion</span>> <<span class="pl-ent">groupId</span>>org.example</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>toolz</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>0.3-SNAPSHOT</<span class="pl-ent">version</span>> <<span class="pl-ent">dependencies</span>> <<span class="pl-ent">dependency</span>> <<span class="pl-ent">groupId</span>>org.tomitribe</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>tomitribe-crest</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>0.3-SNAPSHOT</<span class="pl-ent">version</span>> </<span class="pl-ent">dependency</span>> <<span class="pl-ent">dependency</span>> <<span class="pl-ent">groupId</span>>junit</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>junit</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>4.10</<span class="pl-ent">version</span>> <<span class="pl-ent">scope</span>>test</<span class="pl-ent">scope</span>> </<span class="pl-ent">dependency</span>> <span class="pl-c"><span class="pl-c"><!--</span> Add tomitribe-crest-xbean if you want classpath scanning for @Command <span class="pl-c">--></span></span> <<span class="pl-ent">dependency</span>> <<span class="pl-ent">groupId</span>>org.tomitribe</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>tomitribe-crest-xbean</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>0.3-SNAPSHOT</<span class="pl-ent">version</span>> </<span class="pl-ent">dependency</span>> </<span class="pl-ent">dependencies</span>> <<span class="pl-ent">build</span>> <<span class="pl-ent">defaultGoal</span>>install</<span class="pl-ent">defaultGoal</span>> <<span class="pl-ent">plugins</span>> <<span class="pl-ent">plugin</span>> <<span class="pl-ent">artifactId</span>>maven-shade-plugin</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>2.1</<span class="pl-ent">version</span>> <<span class="pl-ent">executions</span>> <<span class="pl-ent">execution</span>> <<span class="pl-ent">phase</span>>package</<span class="pl-ent">phase</span>> <<span class="pl-ent">goals</span>> <<span class="pl-ent">goal</span>>shade</<span class="pl-ent">goal</span>> </<span class="pl-ent">goals</span>> <<span class="pl-ent">configuration</span>> <<span class="pl-ent">transformers</span>> <<span class="pl-ent">transformer</span> <span class="pl-e">implementation</span>=<span class="pl-s"><span class="pl-pds">"</span>org.apache.maven.plugins.shade.resource.ManifestResourceTransformer<span class="pl-pds">"</span></span>> <<span class="pl-ent">mainClass</span>>org.tomitribe.crest.Main</<span class="pl-ent">mainClass</span>> </<span class="pl-ent">transformer</span>> </<span class="pl-ent">transformers</span>> </<span class="pl-ent">configuration</span>> </<span class="pl-ent">execution</span>> </<span class="pl-ent">executions</span>> </<span class="pl-ent">plugin</span>> </<span class="pl-ent">plugins</span>> </<span class="pl-ent">build</span>> <<span class="pl-ent">repositories</span>> <<span class="pl-ent">repository</span>> <<span class="pl-ent">id</span>>sonatype-nexus-snapshots</<span class="pl-ent">id</span>> <<span class="pl-ent">name</span>>Sonatype Nexus Snapshots</<span class="pl-ent">name</span>> <<span class="pl-ent">url</span>>https://oss.sonatype.org/content/repositories/snapshots</<span class="pl-ent">url</span>> <<span class="pl-ent">releases</span>> <<span class="pl-ent">enabled</span>>false</<span class="pl-ent">enabled</span>> </<span class="pl-ent">releases</span>> <<span class="pl-ent">snapshots</span>> <<span class="pl-ent">enabled</span>>true</<span class="pl-ent">enabled</span>> </<span class="pl-ent">snapshots</span>> </<span class="pl-ent">repository</span>> </<span class="pl-ent">repositories</span>> </<span class="pl-ent">project</span>></pre></div> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-bean-parameter-binding" tabindex="-1" class="heading-element" dir="auto">Bean Parameter Binding</h2><a id="user-content-bean-parameter-binding" class="anchor" aria-label="Permalink: Bean Parameter Binding" href="#bean-parameter-binding"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">If you don’t want to inject in all your commands the same N parameters you can modelize them as an object. Just use standard parameters as constructor parameters of the bean:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public class ColorfulCmd { @Command public static void exec(final Color color) { // ... } }"><pre><span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">ColorfulCmd</span> { <span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-smi">void</span> <span class="pl-en">exec</span>(<span class="pl-k">final</span> <span class="pl-smi">Color</span> <span class="pl-s1">color</span>) { <span class="pl-c">// ...</span> } }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">To identify <code>Color</code> as an "option aware" parameter just decorate it with <code>@Options</code>:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Options public class Color { // getters omitted for brevity private final int r; private final int g; private final int b; private final int a; public Color(@Option("r") @Default("255") final int r, @Option("g") @Default("255") final int g, @Option("b") @Default("255") final int b, @Option("a") @Default("255") final int a) { this.r = r; this.g = g; this.b = b; this.a = a; } }"><pre><span class="pl-c1">@</span><span class="pl-c1">Options</span> <span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">Color</span> { <span class="pl-c">// getters omitted for brevity</span> <span class="pl-k">private</span> <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">r</span>; <span class="pl-k">private</span> <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">g</span>; <span class="pl-k">private</span> <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">b</span>; <span class="pl-k">private</span> <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">a</span>; <span class="pl-k">public</span> <span class="pl-smi">Color</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"r"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"255"</span>) <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">r</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"g"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"255"</span>) <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">g</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"b"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"255"</span>) <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">b</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"a"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"255"</span>) <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">a</span>) { <span class="pl-smi">this</span>.<span class="pl-s1">r</span> = <span class="pl-s1">r</span>; <span class="pl-smi">this</span>.<span class="pl-s1">g</span> = <span class="pl-s1">g</span>; <span class="pl-smi">this</span>.<span class="pl-s1">b</span> = <span class="pl-s1">b</span>; <span class="pl-smi">this</span>.<span class="pl-s1">a</span> = <span class="pl-s1">a</span>; } }</pre></div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h3 id="user-content-prefixing-options" tabindex="-1" class="heading-element" dir="auto">Prefixing options</h3><a id="user-content-prefixing-options" class="anchor" aria-label="Permalink: Prefixing options" href="#prefixing-options"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">If you reuse the same parameter N times you’ll probably want to prefix options. If we take previous example (<code>Params</code>) you can desire to use <code>--background.r</code> and <code>--foreground.r</code> (same for g, b, a).</p> </div> <div dir="auto"> <p dir="auto">Just use <code>@Option</code> in the method parameter to do so:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public class ColorfulCmd { @Command public static void exec(@Option("background.") final Color colorBg, @Option("foreground.") final Color colorFg) { // ... } }"><pre><span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">ColorfulCmd</span> { <span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-smi">void</span> <span class="pl-en">exec</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"background."</span>) <span class="pl-k">final</span> <span class="pl-smi">Color</span> <span class="pl-s1">colorBg</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"foreground."</span>) <span class="pl-k">final</span> <span class="pl-smi">Color</span> <span class="pl-s1">colorFg</span>) { <span class="pl-c">// ...</span> } }</pre></div> </div> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Note</div> </td> <td> the '.' is not automatically added to allow you use to another convention like '-' or '_' ones for instance. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h3 id="user-content-override-defaults" tabindex="-1" class="heading-element" dir="auto">Override defaults</h3><a id="user-content-override-defaults" class="anchor" aria-label="Permalink: Override defaults" href="#override-defaults"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">If you reuse the same parameter model accross command parameter you’ll surely want to override some default in some cases. For that purpose just use <code>@Defaults</code> and define the mappings you want:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public class ColorfulCmd { @Command public static void exec(@Defaults({ @Defaults.DefaultMapping(name = "r", value = "0"), @Defaults.DefaultMapping(name = "g", value = "0"), @Defaults.DefaultMapping(name = "b", value = "0"), @Defaults.DefaultMapping(name = "a", value = "0") }) @Option("background.") final Color colorBg, @Defaults({ @Defaults.DefaultMapping(name = "r", value = "255"), @Defaults.DefaultMapping(name = "g", value = "255"), @Defaults.DefaultMapping(name = "b", value = "255"), @Defaults.DefaultMapping(name = "a", value = "255") }) @Option("foreground.") final Color colorFg) { // ... } }"><pre><span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">ColorfulCmd</span> { <span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-smi">void</span> <span class="pl-en">exec</span>(<span class="pl-c1">@</span><span class="pl-c1">Defaults</span>({ <span class="pl-c1">@</span><span class="pl-smi">Defaults</span>.<span class="pl-s1">DefaultMapping</span>(<span class="pl-s1">name</span> = <span class="pl-s">"r"</span>, <span class="pl-s1">value</span> = <span class="pl-s">"0"</span>), <span class="pl-c1">@</span><span class="pl-smi">Defaults</span>.<span class="pl-s1">DefaultMapping</span>(<span class="pl-s1">name</span> = <span class="pl-s">"g"</span>, <span class="pl-s1">value</span> = <span class="pl-s">"0"</span>), <span class="pl-c1">@</span><span class="pl-smi">Defaults</span>.<span class="pl-s1">DefaultMapping</span>(<span class="pl-s1">name</span> = <span class="pl-s">"b"</span>, <span class="pl-s1">value</span> = <span class="pl-s">"0"</span>), <span class="pl-c1">@</span><span class="pl-smi">Defaults</span>.<span class="pl-s1">DefaultMapping</span>(<span class="pl-s1">name</span> = <span class="pl-s">"a"</span>, <span class="pl-s1">value</span> = <span class="pl-s">"0"</span>) }) <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"background."</span>) <span class="pl-k">final</span> <span class="pl-smi">Color</span> <span class="pl-s1">colorBg</span>, <span class="pl-c1">@</span><span class="pl-c1">Defaults</span>({ <span class="pl-c1">@</span><span class="pl-smi">Defaults</span>.<span class="pl-s1">DefaultMapping</span>(<span class="pl-s1">name</span> = <span class="pl-s">"r"</span>, <span class="pl-s1">value</span> = <span class="pl-s">"255"</span>), <span class="pl-c1">@</span><span class="pl-smi">Defaults</span>.<span class="pl-s1">DefaultMapping</span>(<span class="pl-s1">name</span> = <span class="pl-s">"g"</span>, <span class="pl-s1">value</span> = <span class="pl-s">"255"</span>), <span class="pl-c1">@</span><span class="pl-smi">Defaults</span>.<span class="pl-s1">DefaultMapping</span>(<span class="pl-s1">name</span> = <span class="pl-s">"b"</span>, <span class="pl-s1">value</span> = <span class="pl-s">"255"</span>), <span class="pl-c1">@</span><span class="pl-smi">Defaults</span>.<span class="pl-s1">DefaultMapping</span>(<span class="pl-s1">name</span> = <span class="pl-s">"a"</span>, <span class="pl-s1">value</span> = <span class="pl-s">"255"</span>) }) <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"foreground."</span>) <span class="pl-k">final</span> <span class="pl-smi">Color</span> <span class="pl-s1">colorFg</span>) { <span class="pl-c">// ...</span> } }</pre></div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h3 id="user-content-interceptors" tabindex="-1" class="heading-element" dir="auto">Interceptors</h3><a id="user-content-interceptors" class="anchor" aria-label="Permalink: Interceptors" href="#interceptors"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">Sometimes you need to modify the command invocation or "insert" code before/after the command execution. For that purpose crest has some light interceptor support.</p> </div> <div dir="auto"> <p dir="auto">Defining an interceptor is as easy as defining a class with:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public static class MyInterceptor { @CrestInterceptor public Object intercept(final CrestContext crestContext) { return crestContext.proceed(); } }"><pre><span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-k">class</span> <span class="pl-smi">MyInterceptor</span> { <span class="pl-c1">@</span><span class="pl-c1">CrestInterceptor</span> <span class="pl-k">public</span> <span class="pl-smi">Object</span> <span class="pl-en">intercept</span>(<span class="pl-k">final</span> <span class="pl-smi">CrestContext</span> <span class="pl-s1">crestContext</span>) { <span class="pl-k">return</span> <span class="pl-s1">crestContext</span>.<span class="pl-en">proceed</span>(); } }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">The constraint for an interceptor are:</p> </div> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto">being decorated with <code>@CrestInterceptor</code></p> </li> <li> <p dir="auto">the method needs to be public</p> </li> <li> <p dir="auto">the method needs to table a single parameter of type <code>CrestContext</code></p> </li> </ul> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Note</div> </td> <td> you can pass <code>@CrestInterceptor</code> a value changing the key used to mark the interceptor. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> <div dir="auto"> <p dir="auto">To let a command use an interceptor or multiple ones just list them ordered in <code>interceptedBy</code> parameter:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command(interceptedBy = { MySecurityInterceptor.class, MyLoggingInterceptor.class, MyParameterFillingInterceptor.class }) public void test1( @Option("o1") final String o1, @Option("o2") final int o2, @Err final PrintStream err, @Out final PrintStream out, @In final InputStream is, @Option("o3") final String o3, final URL url) { // do something }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span>(<span class="pl-s1">interceptedBy</span> = { <span class="pl-smi">MySecurityInterceptor</span>.<span class="pl-k">class</span>, <span class="pl-smi">MyLoggingInterceptor</span>.<span class="pl-k">class</span>, <span class="pl-smi">MyParameterFillingInterceptor</span>.<span class="pl-k">class</span> }) <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">test1</span>( <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"o1"</span>) <span class="pl-k">final</span> <span class="pl-smi">String</span> <span class="pl-s1">o1</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"o2"</span>) <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">o2</span>, <span class="pl-c1">@</span><span class="pl-c1">Err</span> <span class="pl-k">final</span> <span class="pl-smi">PrintStream</span> <span class="pl-s1">err</span>, <span class="pl-c1">@</span><span class="pl-c1">Out</span> <span class="pl-k">final</span> <span class="pl-smi">PrintStream</span> <span class="pl-s1">out</span>, <span class="pl-c1">@</span><span class="pl-c1">In</span> <span class="pl-k">final</span> <span class="pl-smi">InputStream</span> <span class="pl-s1">is</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"o3"</span>) <span class="pl-k">final</span> <span class="pl-smi">String</span> <span class="pl-s1">o3</span>, <span class="pl-k">final</span> <span class="pl-smi">URL</span> <span class="pl-s1">url</span>) { <span class="pl-c">// do something</span> }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">Crest supports 3 styles of declaring interceptors</p> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h4 id="user-content-via-commandinterceptedby" tabindex="-1" class="heading-element" dir="auto">Via <code>@Command(interceptedBy)</code></h4><a id="user-content-via-commandinterceptedby" class="anchor" aria-label="Permalink: Via @Command(interceptedBy)" href="#via-commandinterceptedby"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">The <code>@Command</code> declaration uses the <code>interceptedBy</code> attribute to name the interceptor class.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public static class Foo { @Command(interceptedBy = GreenInterceptor.class) public String fighters(final String arg) { return arg; }"><pre><span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-k">class</span> <span class="pl-smi">Foo</span> { <span class="pl-c1">@</span><span class="pl-c1">Command</span>(<span class="pl-s1">interceptedBy</span> = <span class="pl-smi">GreenInterceptor</span>.<span class="pl-k">class</span>) <span class="pl-k">public</span> <span class="pl-smi">String</span> <span class="pl-en">fighters</span>(<span class="pl-k">final</span> <span class="pl-smi">String</span> <span class="pl-s1">arg</span>) { <span class="pl-k">return</span> <span class="pl-s1">arg</span>; }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">The <code>GreenInterceptor</code> definition is as usual</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public class GreenInterceptor { @CrestInterceptor public Object intercept(final CrestContext crestContext) { return crestContext.proceed(); } }"><pre><span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">GreenInterceptor</span> { <span class="pl-c1">@</span><span class="pl-c1">CrestInterceptor</span> <span class="pl-k">public</span> <span class="pl-smi">Object</span> <span class="pl-en">intercept</span>(<span class="pl-k">final</span> <span class="pl-smi">CrestContext</span> <span class="pl-s1">crestContext</span>) { <span class="pl-k">return</span> <span class="pl-s1">crestContext</span>.<span class="pl-en">proceed</span>(); } }</pre></div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h4 id="user-content-custom-annotation-containing-crestinterceptorfoointerceptor-class" tabindex="-1" class="heading-element" dir="auto">Custom annotation containing <code>@CrestInterceptor(FooInterceptor.class)</code></h4><a id="user-content-custom-annotation-containing-crestinterceptorfoointerceptorclass" class="anchor" aria-label="Permalink: Custom annotation containing @CrestInterceptor(FooInterceptor.class)" href="#custom-annotation-containing-crestinterceptorfoointerceptorclass"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">In this style, we define our own custom annotation <code>@Red</code> that names <code>RedInterceptor</code> directly</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@CrestInterceptor(RedInterceptor.class) @Retention(value = RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Red { }"><pre><span class="pl-c1">@</span><span class="pl-c1">CrestInterceptor</span>(<span class="pl-smi">RedInterceptor</span>.<span class="pl-k">class</span>) <span class="pl-c1">@</span><span class="pl-c1">Retention</span>(<span class="pl-s1">value</span> = <span class="pl-smi">RetentionPolicy</span>.<span class="pl-c1">RUNTIME</span>) <span class="pl-c1">@</span><span class="pl-c1">Target</span>({<span class="pl-smi">ElementType</span>.<span class="pl-c1">METHOD</span>}) <span class="pl-k">public</span> @interface <span class="pl-s1">Red</span> { }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">…and use it on our <code>@Command</code> method as follows</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public static class Foo { @Red @Command public String fighters(final String arg) { return arg; }"><pre><span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-k">class</span> <span class="pl-smi">Foo</span> { <span class="pl-c1">@</span><span class="pl-c1">Red</span> <span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">String</span> <span class="pl-en">fighters</span>(<span class="pl-k">final</span> <span class="pl-smi">String</span> <span class="pl-s1">arg</span>) { <span class="pl-k">return</span> <span class="pl-s1">arg</span>; }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">The <code>RedInterceptor</code> definition is as usual</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public class RedInterceptor { @CrestInterceptor public Object intercept(final CrestContext crestContext) { return crestContext.proceed(); } }"><pre><span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">RedInterceptor</span> { <span class="pl-c1">@</span><span class="pl-c1">CrestInterceptor</span> <span class="pl-k">public</span> <span class="pl-smi">Object</span> <span class="pl-en">intercept</span>(<span class="pl-k">final</span> <span class="pl-smi">CrestContext</span> <span class="pl-s1">crestContext</span>) { <span class="pl-k">return</span> <span class="pl-s1">crestContext</span>.<span class="pl-en">proceed</span>(); } }</pre></div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h4 id="user-content-custom-annotation-containing-crestinterceptor-loosely-coupled-to-an-implementation" tabindex="-1" class="heading-element" dir="auto">Custom annotation containing <code>@CrestInterceptor</code> loosely coupled to an implementation</h4><a id="user-content-custom-annotation-containing-crestinterceptor-loosely-coupled-to-an-implementation" class="anchor" aria-label="Permalink: Custom annotation containing @CrestInterceptor loosely coupled to an implementation" href="#custom-annotation-containing-crestinterceptor-loosely-coupled-to-an-implementation"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">In this style, we define our own custom annotation <code>@Blue</code>, but it is not bound to a specific implementation. The <code>@CrestInterceptor</code> does not mention the class.</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@CrestInterceptor @Retention(value = RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Blue { }"><pre><span class="pl-c1">@</span><span class="pl-c1">CrestInterceptor</span> <span class="pl-c1">@</span><span class="pl-c1">Retention</span>(<span class="pl-s1">value</span> = <span class="pl-smi">RetentionPolicy</span>.<span class="pl-c1">RUNTIME</span>) <span class="pl-c1">@</span><span class="pl-c1">Target</span>({<span class="pl-smi">ElementType</span>.<span class="pl-c1">METHOD</span>}) <span class="pl-k">public</span> @interface <span class="pl-s1">Blue</span> { }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">The <code>@Blue</code> is used on our <code>@Command</code> method as in the previous example</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public static class Foo { @Blue @Command public String fighters(final String arg) { return arg; }"><pre><span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-k">class</span> <span class="pl-smi">Foo</span> { <span class="pl-c1">@</span><span class="pl-c1">Blue</span> <span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">String</span> <span class="pl-en">fighters</span>(<span class="pl-k">final</span> <span class="pl-smi">String</span> <span class="pl-s1">arg</span>) { <span class="pl-k">return</span> <span class="pl-s1">arg</span>; }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">The <code>BlueInterceptor</code> definition identifies itself as the implementation of <code>@Blue</code> by using that annotation on its class</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Blue public class BlueInterceptor { @CrestInterceptor public Object intercept(final CrestContext crestContext) { return crestContext.proceed(); } }"><pre><span class="pl-c1">@</span><span class="pl-c1">Blue</span> <span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">BlueInterceptor</span> { <span class="pl-c1">@</span><span class="pl-c1">CrestInterceptor</span> <span class="pl-k">public</span> <span class="pl-smi">Object</span> <span class="pl-en">intercept</span>(<span class="pl-k">final</span> <span class="pl-smi">CrestContext</span> <span class="pl-s1">crestContext</span>) { <span class="pl-k">return</span> <span class="pl-s1">crestContext</span>.<span class="pl-en">proceed</span>(); } }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">This can be useful if you create an API jar where <code>@Blue</code> might be contained, but you want to put the implementation in a different jar. Perhaps there are different implementations, each it it’s own jar, and people choose the implementation they want by including the desired implementation jar in the classpath.</p> </div> <div dir="auto"> <p dir="auto">In this approach, however, it is necessary to ensure <code>BlueInterceptor.class</code> is visible to Crest by creating a <code>Loader</code> implementation such as the following</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="package org.example.myapp; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; public class Loader implements org.tomitribe.crest.api.Loader { @Override public Iterator<Class<?>> iterator() { final List<Class<?>> classes = new ArrayList<>(); classes.add(BlueInterceptor.class); return classes.listIterator(); } }"><pre><span class="pl-k">package</span> <span class="pl-s1">org</span>.<span class="pl-s1">example</span>.<span class="pl-s1">myapp</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">ArrayList</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">Arrays</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">Collections</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">Iterator</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">List</span>; <span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">Loader</span> <span class="pl-k">implements</span> <span class="pl-smi">org</span>.<span class="pl-smi">tomitribe</span>.<span class="pl-smi">crest</span>.<span class="pl-smi">api</span>.<span class="pl-smi">Loader</span> { <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">Iterator</span><<span class="pl-smi">Class</span><?>> <span class="pl-en">iterator</span>() { <span class="pl-k">final</span> <span class="pl-smi">List</span><<span class="pl-smi">Class</span><?>> <span class="pl-s1">classes</span> = <span class="pl-k">new</span> <span class="pl-smi">ArrayList</span><>(); <span class="pl-s1">classes</span>.<span class="pl-en">add</span>(<span class="pl-smi">BlueInterceptor</span>.<span class="pl-k">class</span>); <span class="pl-k">return</span> <span class="pl-s1">classes</span>.<span class="pl-en">listIterator</span>(); } }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">and declaring it in the jar at <code>META-INF/services/org.tomitribe.crest.api.Loader</code> with the following contents:</p> </div> <div dir="auto"> <div dir="auto"> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="org.example.myapp.Loader"><pre class="notranslate"><code>org.example.myapp.Loader</code></pre></div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h4 id="user-content-example-for-security" tabindex="-1" class="heading-element" dir="auto">Example for security</h4><a id="user-content-example-for-security" class="anchor" aria-label="Permalink: Example for security" href="#example-for-security"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">Crest provides a <code>org.tomitribe.crest.interceptor.security.SecurityInterceptor</code> which handles <code>@RolesAllowed</code> using the SPI <code>org.tomitribe.crest.interceptor.security.RoleProvider</code> to determine if you can call or not the command contextually.</p> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Note</div> </td> <td> <code>RoleProvider</code> is taken from <code>Environment</code> services. You can register it through <code>org.tomitribe.crest.environments.SystemEnvironment</code> constructor and just set it as environment on <code>org.tomitribe.crest.environments.Environment.ENVIRONMENT_THREAD_LOCAL</code>. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> <div dir="auto"> <p dir="auto">Here a sample command using it:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@RolesAllowed("test") @Command(interceptedBy = SecurityInterceptor.class) public static String val() { return "ok"; }"><pre><span class="pl-c1">@</span><span class="pl-c1">RolesAllowed</span>(<span class="pl-s">"test"</span>) <span class="pl-c1">@</span><span class="pl-c1">Command</span>(<span class="pl-s1">interceptedBy</span> = <span class="pl-smi">SecurityInterceptor</span>.<span class="pl-k">class</span>) <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-smi">String</span> <span class="pl-en">val</span>() { <span class="pl-k">return</span> <span class="pl-s">"ok"</span>; }</pre></div> </div> </div> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-maven-archetype" tabindex="-1" class="heading-element" dir="auto">Maven Archetype</h2><a id="user-content-maven-archetype" class="anchor" aria-label="Permalink: Maven Archetype" href="#maven-archetype"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">A maven archetype is available to quickly bootstrap small projects complete with the a pom like the above. Save yourself some time on copy/paste then find/replace.</p> </div> <div dir="auto"> <div dir="auto"> <pre>mvn archetype:generate \ -DarchetypeGroupId=org.tomitribe \ -DarchetypeArtifactId=tomitribe-crest-archetype \ -DarchetypeVersion=1.0.0-SNAPSHOT</pre> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-maven-plugin" tabindex="-1" class="heading-element" dir="auto">Maven Plugin</h2><a id="user-content-maven-plugin" class="anchor" aria-label="Permalink: Maven Plugin" href="#maven-plugin"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">If you don’t want to rely on runtime scanning to find classes but still want to avoid to list command classes or just reuse crest Main you can use Maven Plugin to find it and generate a descriptor used to load classes.</p> </div> <div dir="auto"> <p dir="auto">Here is how to define it in your pom:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-text-xml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="<plugin> <groupId>org.tomitribe</groupId> <version>${crest.version}</version> <artifactId>crest-maven-plugin</artifactId> <executions> <execution> <goals> <goal>descriptor</goal> </goals> </execution> </executions> </plugin>"><pre><<span class="pl-ent">plugin</span>> <<span class="pl-ent">groupId</span>>org.tomitribe</<span class="pl-ent">groupId</span>> <<span class="pl-ent">version</span>>${crest.version}</<span class="pl-ent">version</span>> <<span class="pl-ent">artifactId</span>>crest-maven-plugin</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">executions</span>> <<span class="pl-ent">execution</span>> <<span class="pl-ent">goals</span>> <<span class="pl-ent">goal</span>>descriptor</<span class="pl-ent">goal</span>> </<span class="pl-ent">goals</span>> </<span class="pl-ent">execution</span>> </<span class="pl-ent">executions</span>> </<span class="pl-ent">plugin</span>></pre></div> </div> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-deltaspike-annotation-processor" tabindex="-1" class="heading-element" dir="auto">DeltaSpike Annotation Processor</h2><a id="user-content-deltaspike-annotation-processor" class="anchor" aria-label="Permalink: DeltaSpike Annotation Processor" href="#deltaspike-annotation-processor"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">Adding this dependency to your project:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-text-xml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="<dependency> <groupId>org.tomitribe</groupId> <artifactId>tomitribe-crest-generator</artifactId> <version>${crest.version}</version> <scope>provided</scope> </dependency>"><pre><<span class="pl-ent">dependency</span>> <<span class="pl-ent">groupId</span>>org.tomitribe</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>tomitribe-crest-generator</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>${crest.version}</<span class="pl-ent">version</span>> <<span class="pl-ent">scope</span>>provided</<span class="pl-ent">scope</span>> </<span class="pl-ent">dependency</span>></pre></div> </div> </div> <div dir="auto"> <p dir="auto">Crest Generator can integrates with DeltaSpike to generate binding pojo. It will split <code>@ConfigProperty</code> on first dot and create one binding per prefix.</p> </div> <div dir="auto"> <p dir="auto">Here is an example:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public class DeltaspikeBean { @Inject @ConfigProperty(name = "app.service.base", defaultValue = "http://localhost:8080") private String base; @Inject @ConfigProperty(name = "app.service.retries") private Integer retries; }"><pre><span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">DeltaspikeBean</span> { <span class="pl-c1">@</span><span class="pl-c1">Inject</span> <span class="pl-c1">@</span><span class="pl-c1">ConfigProperty</span>(<span class="pl-s1">name</span> = <span class="pl-s">"app.service.base"</span>, <span class="pl-s1">defaultValue</span> = <span class="pl-s">"http://localhost:8080"</span>) <span class="pl-k">private</span> <span class="pl-smi">String</span> <span class="pl-s1">base</span>; <span class="pl-c1">@</span><span class="pl-c1">Inject</span> <span class="pl-c1">@</span><span class="pl-c1">ConfigProperty</span>(<span class="pl-s1">name</span> = <span class="pl-s">"app.service.retries"</span>) <span class="pl-k">private</span> <span class="pl-smi">Integer</span> <span class="pl-s1">retries</span>; }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">It will generate the following binding:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="package org.tomitribe.crest.generator.generated; import java.util.Collections; import java.util.Map; import java.util.HashMap; import org.apache.deltaspike.core.api.config.ConfigResolver; import org.apache.deltaspike.core.spi.config.ConfigSource; import org.tomitribe.crest.api.Default; import org.tomitribe.crest.api.Option; import static java.util.Collections.singletonList; public class App { private String serviceBase; private Integer serviceRetries; public App( @Option("service-base") @Default("http://localhost:8080") String serviceBase, @Option("service-retries") Integer serviceRetries) { final Map<String, String> ____properties = new HashMap<>(); this.serviceBase = serviceBase; ____properties.put("app.service.base", String.valueOf(serviceBase)); this.serviceRetries = serviceRetries; ____properties.put("app.service.retries", String.valueOf(serviceRetries)); ConfigResolver.addConfigSources(Collections.<ConfigSource>singletonList(new ConfigSource() { @Override public int getOrdinal() { return 0; } @Override public Map<String, String> getProperties() { return ____properties; } @Override public String getPropertyValue(final String key) { return ____properties.get(key); } @Override public String getConfigName() { return "crest-app"; } @Override public boolean isScannable() { return true; } })); } public String getServiceBase() { return serviceBase; } public void setServiceBase(final String serviceBase) { this.serviceBase = serviceBase; } public Integer getServiceRetries() { return serviceRetries; } public void setServiceRetries(final Integer serviceRetries) { this.serviceRetries = serviceRetries; } }"><pre><span class="pl-k">package</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">generator</span>.<span class="pl-s1">generated</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">Collections</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">Map</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">HashMap</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">apache</span>.<span class="pl-s1">deltaspike</span>.<span class="pl-s1">core</span>.<span class="pl-s1">api</span>.<span class="pl-s1">config</span>.<span class="pl-s1">ConfigResolver</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">apache</span>.<span class="pl-s1">deltaspike</span>.<span class="pl-s1">core</span>.<span class="pl-s1">spi</span>.<span class="pl-s1">config</span>.<span class="pl-s1">ConfigSource</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">api</span>.<span class="pl-s1">Default</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">api</span>.<span class="pl-s1">Option</span>; <span class="pl-k">import</span> <span class="pl-k">static</span> <span class="pl-s1">java</span>.<span class="pl-s1">util</span>.<span class="pl-s1">Collections</span>.<span class="pl-s1">singletonList</span>; <span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">App</span> { <span class="pl-k">private</span> <span class="pl-smi">String</span> <span class="pl-s1">serviceBase</span>; <span class="pl-k">private</span> <span class="pl-smi">Integer</span> <span class="pl-s1">serviceRetries</span>; <span class="pl-k">public</span> <span class="pl-smi">App</span>( <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"service-base"</span>) <span class="pl-c1">@</span><span class="pl-c1">Default</span>(<span class="pl-s">"http://localhost:8080"</span>) <span class="pl-smi">String</span> <span class="pl-s1">serviceBase</span>, <span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"service-retries"</span>) <span class="pl-smi">Integer</span> <span class="pl-s1">serviceRetries</span>) { <span class="pl-k">final</span> <span class="pl-smi">Map</span><<span class="pl-smi">String</span>, <span class="pl-smi">String</span>> <span class="pl-s1">____properties</span> = <span class="pl-k">new</span> <span class="pl-smi">HashMap</span><>(); <span class="pl-smi">this</span>.<span class="pl-s1">serviceBase</span> = <span class="pl-s1">serviceBase</span>; <span class="pl-s1">____properties</span>.<span class="pl-en">put</span>(<span class="pl-s">"app.service.base"</span>, <span class="pl-smi">String</span>.<span class="pl-en">valueOf</span>(<span class="pl-s1">serviceBase</span>)); <span class="pl-smi">this</span>.<span class="pl-s1">serviceRetries</span> = <span class="pl-s1">serviceRetries</span>; <span class="pl-s1">____properties</span>.<span class="pl-en">put</span>(<span class="pl-s">"app.service.retries"</span>, <span class="pl-smi">String</span>.<span class="pl-en">valueOf</span>(<span class="pl-s1">serviceRetries</span>)); <span class="pl-smi">ConfigResolver</span>.<span class="pl-en">addConfigSources</span>(<span class="pl-smi">Collections</span>.<<span class="pl-smi">ConfigSource</span>><span class="pl-en">singletonList</span>(<span class="pl-k">new</span> <span class="pl-smi">ConfigSource</span>() { <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">int</span> <span class="pl-en">getOrdinal</span>() { <span class="pl-k">return</span> <span class="pl-c1">0</span>; } <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">Map</span><<span class="pl-smi">String</span>, <span class="pl-smi">String</span>> <span class="pl-en">getProperties</span>() { <span class="pl-k">return</span> <span class="pl-s1">____properties</span>; } <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">String</span> <span class="pl-en">getPropertyValue</span>(<span class="pl-k">final</span> <span class="pl-smi">String</span> <span class="pl-s1">key</span>) { <span class="pl-k">return</span> <span class="pl-s1">____properties</span>.<span class="pl-en">get</span>(<span class="pl-s1">key</span>); } <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">String</span> <span class="pl-en">getConfigName</span>() { <span class="pl-k">return</span> <span class="pl-s">"crest-app"</span>; } <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">public</span> <span class="pl-smi">boolean</span> <span class="pl-en">isScannable</span>() { <span class="pl-k">return</span> <span class="pl-c1">true</span>; } })); } <span class="pl-k">public</span> <span class="pl-smi">String</span> <span class="pl-en">getServiceBase</span>() { <span class="pl-k">return</span> <span class="pl-s1">serviceBase</span>; } <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">setServiceBase</span>(<span class="pl-k">final</span> <span class="pl-smi">String</span> <span class="pl-s1">serviceBase</span>) { <span class="pl-smi">this</span>.<span class="pl-s1">serviceBase</span> = <span class="pl-s1">serviceBase</span>; } <span class="pl-k">public</span> <span class="pl-smi">Integer</span> <span class="pl-en">getServiceRetries</span>() { <span class="pl-k">return</span> <span class="pl-s1">serviceRetries</span>; } <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">setServiceRetries</span>(<span class="pl-k">final</span> <span class="pl-smi">Integer</span> <span class="pl-s1">serviceRetries</span>) { <span class="pl-smi">this</span>.<span class="pl-s1">serviceRetries</span> = <span class="pl-s1">serviceRetries</span>; } }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">Then you just need to reuse it ad a crest command parameter:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Command public void myCommand(@Option("app-") final App app) { // ... }"><pre><span class="pl-c1">@</span><span class="pl-c1">Command</span> <span class="pl-k">public</span> <span class="pl-smi">void</span> <span class="pl-en">myCommand</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"app-"</span>) <span class="pl-k">final</span> <span class="pl-smi">App</span> <span class="pl-s1">app</span>) { <span class="pl-c">// ...</span> }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">The nice thing is it will integrate with crest of course but also with DeltaSpike. It means the previous code will also make DeltaSpike injection respecting <code>App</code> configuration (<code>--app-service-base=… --app-service-retries=3</code> for instance).</p> </div> <div dir="auto"> <p dir="auto">If you create a fatjar using TomEE embedded it means you can handle all your DeltaSpike configuration this way and you just need to write a TomEE Embedded runner to get DeltaSpike configuration wired from the command line:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import org.apache.tomee.embedded.Main; public final class Runner { @Command("run") public static void run(@Option("app-") App app) { Main.main(new String[] { "--as-war", "--single-classloader" } /*fatjar "as war" deployment*/); // automatically @Inject @ConfigProperty will be populated :) } }"><pre><span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">apache</span>.<span class="pl-s1">tomee</span>.<span class="pl-s1">embedded</span>.<span class="pl-s1">Main</span>; <span class="pl-k">public</span> <span class="pl-k">final</span> <span class="pl-k">class</span> <span class="pl-smi">Runner</span> { <span class="pl-c1">@</span><span class="pl-c1">Command</span>(<span class="pl-s">"run"</span>) <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-smi">void</span> <span class="pl-en">run</span>(<span class="pl-c1">@</span><span class="pl-c1">Option</span>(<span class="pl-s">"app-"</span>) <span class="pl-smi">App</span> <span class="pl-s1">app</span>) { <span class="pl-smi">Main</span>.<span class="pl-en">main</span>(<span class="pl-k">new</span> <span class="pl-smi">String</span>[] { <span class="pl-s">"--as-war"</span>, <span class="pl-s">"--single-classloader"</span> } <span class="pl-c">/*fatjar "as war" deployment*/</span>); <span class="pl-c">// automatically @Inject @ConfigProperty will be populated :)</span> } }</pre></div> </div> </div> <div dir="auto"> <p dir="auto">Potential enhancement(s):</p> </div> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto">option to generate TomEE Embedded main?</p> </li> <li> <p dir="auto">Tamaya integration on the same model?</p> </li> <li> <p dir="auto">Owner integration</p> </li> <li> <p dir="auto">…</p> </li> </ul> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-cli-module" tabindex="-1" class="heading-element" dir="auto">Cli module</h2><a id="user-content-cli-module" class="anchor" aria-label="Permalink: Cli module" href="#cli-module"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">Cli module aims to provide a basic integration with JLine.</p> </div> <div dir="auto"> <p dir="auto">All starts from <code>org.tomitribe.crest.cli.api.CrestCli</code> class. Current version is extensible through inheritance but already provides:</p> </div> <div dir="auto"> <ul dir="auto"> <li> <p dir="auto">support of maven plugin commands (crest-commands.txt)</p> </li> <li> <p dir="auto">JLine integration</p> </li> <li> <p dir="auto">Basic pipping support (<code>mycommand | jgrep foo</code>)</p> </li> <li> <p dir="auto">History support is you return a file in <code>org.tomitribe.crest.cli.api.CrestCli.cliHistoryFile</code></p> </li> <li> <p dir="auto"><code>org.tomitribe.crest.cli.api.interceptor.interactive.Interactivable</code> can be used to mark a parameter as required but compatible with interactive mode (ie the parameter is asked in interactive mode if missing).</p> </li> </ul> </div> <div dir="auto"> <p dir="auto">Sample usage:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="final CrestCli cli = new CrestCli(); cli.run();"><pre><span class="pl-k">final</span> <span class="pl-smi">CrestCli</span> <span class="pl-s1">cli</span> = <span class="pl-k">new</span> <span class="pl-smi">CrestCli</span>(); <span class="pl-s1">cli</span>.<span class="pl-en">run</span>();</pre></div> </div> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Tip</div> </td> <td> <code>CrestCli</code> also has a <code>main(String[])</code> so it can be used directly as well. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Note</div> </td> <td> if you don’t provide an <code>exit</code> command one is added by default. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h2 id="user-content-graalvm-integration" tabindex="-1" class="heading-element" dir="auto">GraalVM integration</h2><a id="user-content-graalvm-integration" class="anchor" aria-label="Permalink: GraalVM integration" href="#graalvm-integration"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto"> <p dir="auto">Tomitribe Crest works very smoothly with GraalVM enabling you to get a native binary from your CLI.</p> </div> <div dir="auto"> <p dir="auto">You can do it writing manually your <code>reflections.json</code> but you can also do it through maven using <code>Apache Geronimo Arthur</code> plugin. In this last case, you can set up your CLI auto-configuration with this setup:</p> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Important</div> </td> <td> requires Tomitribe Crest >= 0.17. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-text-xml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="<plugin> <groupId>org.apache.geronimo.arthur</groupId> <artifactId>arthur-maven-plugin</artifactId> <version>1.0.3</version> <configuration> <graalVersion>21.3.0.r17</graalVersion> (1) <main>org.tomitribe.crest.Main</main> (2) <extensionProperties> (4) <!-- starts with, excludes exists too, don't forget help if you don't override it yourself --> <tomitribe.crest.command.includes> com.superbiz.command, org.tomitribe.crest.cmds.processors.Help </tomitribe.crest.command.includes> <!-- <tomitribe.crest.editors.includes>....</tomitribe.crest.editors.includes> --> (5) </extensionProperties> <enableAllSecurityServices>false</enableAllSecurityServices> (6) </configuration> <dependencies> (3) <!-- enable crest auto registration for commands/interceptors --> <dependencies> <dependency> <groupId>org.tomitribe</groupId> <artifactId>tomitribe-crest-arthur-extension</artifactId> <version>${crest.version}</version> </dependency> </dependencies> </dependencies> </plugin>"><pre><<span class="pl-ent">plugin</span>> <<span class="pl-ent">groupId</span>>org.apache.geronimo.arthur</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>arthur-maven-plugin</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>1.0.3</<span class="pl-ent">version</span>> <<span class="pl-ent">configuration</span>> <<span class="pl-ent">graalVersion</span>>21.3.0.r17</<span class="pl-ent">graalVersion</span>> (1) <<span class="pl-ent">main</span>>org.tomitribe.crest.Main</<span class="pl-ent">main</span>> (2) <<span class="pl-ent">extensionProperties</span>> (4) <span class="pl-c"><span class="pl-c"><!--</span> starts with, excludes exists too, don't forget help if you don't override it yourself <span class="pl-c">--></span></span> <<span class="pl-ent">tomitribe</span>.crest.command.includes> com.superbiz.command, org.tomitribe.crest.cmds.processors.Help </<span class="pl-ent">tomitribe</span>.crest.command.includes> <span class="pl-c"><span class="pl-c"><!--</span> <tomitribe.crest.editors.includes>....</tomitribe.crest.editors.includes> <span class="pl-c">--></span></span> (5) </<span class="pl-ent">extensionProperties</span>> <<span class="pl-ent">enableAllSecurityServices</span>>false</<span class="pl-ent">enableAllSecurityServices</span>> (6) </<span class="pl-ent">configuration</span>> <<span class="pl-ent">dependencies</span>> (3) <span class="pl-c"><span class="pl-c"><!--</span> enable crest auto registration for commands/interceptors <span class="pl-c">--></span></span> <<span class="pl-ent">dependencies</span>> <<span class="pl-ent">dependency</span>> <<span class="pl-ent">groupId</span>>org.tomitribe</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>tomitribe-crest-arthur-extension</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>${crest.version}</<span class="pl-ent">version</span>> </<span class="pl-ent">dependency</span>> </<span class="pl-ent">dependencies</span>> </<span class="pl-ent">dependencies</span>> </<span class="pl-ent">plugin</span>></pre></div> </div> </div> <div dir="auto"> <ol dir="auto"> <li> <p dir="auto">Ensure to adjust the Graal and JVM base version (here Graal 21.3.0 in its Java 17 flavor),</p> </li> <li> <p dir="auto">Reuse default Crest main,</p> </li> <li> <p dir="auto">Enable crest extension for Arthur,</p> </li> <li> <p dir="auto">Customize the command scanning, note that you can tune the includes/excludes and the values are comma separated and use a "start with" matching logic,</p> </li> <li> <p dir="auto">If you are using <code>@Editor</code>, you can control the scanning there too similarly to commands,</p> </li> <li> <p dir="auto">This option is deprecated in recent graal versions so avoid a warning using a recent version, no direct link with crest itself,</p> </li> </ol> </div> <div dir="auto"> <p dir="auto">Then just run: <code>mvn install arthur:native-image</code> and you will get a <code>target/<artifctId>.graal.bin</code> binary you can share and execute on the built platform.</p> </div> <div dir="auto"> <markdown-accessiblity-table><table> <tbody><tr> <td> <div dir="auto">Important</div> </td> <td> only scanned editors (<code>@Editor</code>) are handled by the extension, SPI ones (<code>META-INF/services/org.tomitribe.crest.api.Editor</code>) can be used if you register them within GraalVM configuration and enable <code>ServiceLoader</code>. </td> </tr> </tbody></table></markdown-accessiblity-table> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h3 id="user-content-graalvm-example" tabindex="-1" class="heading-element" dir="auto">GraalVM example</h3><a id="user-content-graalvm-example" class="anchor" aria-label="Permalink: GraalVM example" href="#graalvm-example"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <div dir="auto">Cat.java</div> <div dir="auto"> <div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="package org.superbiz.crest.demo; import org.tomitribe.crest.api.Command; import org.tomitribe.crest.api.Editor; import org.tomitribe.util.editor.AbstractConverter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class Cat { @Command(usage = "Cat a file.") public String cat(final Path file) throws IOException { return Files.readString(file); } @Editor(Path.class) public static class PathEditor extends AbstractConverter { @Override protected Object toObjectImpl(final String text) { return Paths.get(text); } } }"><pre><span class="pl-k">package</span> <span class="pl-s1">org</span>.<span class="pl-s1">superbiz</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">demo</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">api</span>.<span class="pl-s1">Command</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">crest</span>.<span class="pl-s1">api</span>.<span class="pl-s1">Editor</span>; <span class="pl-k">import</span> <span class="pl-s1">org</span>.<span class="pl-s1">tomitribe</span>.<span class="pl-s1">util</span>.<span class="pl-s1">editor</span>.<span class="pl-s1">AbstractConverter</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">io</span>.<span class="pl-s1">IOException</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">nio</span>.<span class="pl-s1">file</span>.<span class="pl-s1">Files</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">nio</span>.<span class="pl-s1">file</span>.<span class="pl-s1">Path</span>; <span class="pl-k">import</span> <span class="pl-s1">java</span>.<span class="pl-s1">nio</span>.<span class="pl-s1">file</span>.<span class="pl-s1">Paths</span>; <span class="pl-k">public</span> <span class="pl-k">class</span> <span class="pl-smi">Cat</span> { <span class="pl-c1">@</span><span class="pl-c1">Command</span>(<span class="pl-s1">usage</span> = <span class="pl-s">"Cat a file."</span>) <span class="pl-k">public</span> <span class="pl-smi">String</span> <span class="pl-en">cat</span>(<span class="pl-k">final</span> <span class="pl-smi">Path</span> <span class="pl-s1">file</span>) <span class="pl-k">throws</span> <span class="pl-smi">IOException</span> { <span class="pl-k">return</span> <span class="pl-smi">Files</span>.<span class="pl-en">readString</span>(<span class="pl-s1">file</span>); } <span class="pl-c1">@</span><span class="pl-c1">Editor</span>(<span class="pl-smi">Path</span>.<span class="pl-k">class</span>) <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-k">class</span> <span class="pl-smi">PathEditor</span> <span class="pl-k">extends</span> <span class="pl-smi">AbstractConverter</span> { <span class="pl-c1">@</span><span class="pl-c1">Override</span> <span class="pl-k">protected</span> <span class="pl-smi">Object</span> <span class="pl-en">toObjectImpl</span>(<span class="pl-k">final</span> <span class="pl-smi">String</span> <span class="pl-s1">text</span>) { <span class="pl-k">return</span> <span class="pl-smi">Paths</span>.<span class="pl-en">get</span>(<span class="pl-s1">text</span>); } } }</pre></div> </div> </div> <div dir="auto"> <div dir="auto">pom.xml</div> <div dir="auto"> <div class="highlight highlight-text-xml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.superbiz.demo</groupId> <artifactId>demo-crest-arthur</artifactId> <version>1.0-SNAPSHOT</version> <properties> <crest.version>...</crest.version> <!-- >= 0.17 --> </properties> <dependencies> <dependency> <groupId>org.tomitribe</groupId> <artifactId>tomitribe-crest</artifactId> <version>${crest.vesion}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.2.0</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <release>17</release> <source>17</source> <target>17</target> </configuration> </plugin> <plugin> <groupId>org.apache.geronimo.arthur</groupId> <artifactId>arthur-maven-plugin</artifactId> <version>1.0.3</version> <configuration> <graalVersion>21.3.0.r17</graalVersion> <main>org.tomitribe.crest.Main</main> <graalExtensions> <!-- enable crest auto registration for commands/interceptors --> <graalExtension>org.tomitribe:tomitribe-crest-arthur-extension:0.17-SNAPSHOT</graalExtension> </graalExtensions> <extensionProperties> <!-- starts with, excludes exists too --> <tomitribe.crest.command.includes> ${project.groupId}., org.tomitribe.crest.cmds.processors.Help </tomitribe.crest.command.includes> </extensionProperties> <!-- this option is deprecated in recent graal versions --> <enableAllSecurityServices>false</enableAllSecurityServices> </configuration> <dependencies> <dependency> <!-- force arthur to support java 17 --> <groupId>org.apache.xbean</groupId> <artifactId>xbean-asm9-shaded</artifactId> <version>4.20</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>"><pre><?<span class="pl-ent">xml</span><span class="pl-e"> version</span>=<span class="pl-s"><span class="pl-pds">"</span>1.0<span class="pl-pds">"</span></span><span class="pl-e"> encoding</span>=<span class="pl-s"><span class="pl-pds">"</span>UTF-8<span class="pl-pds">"</span></span>?> <<span class="pl-ent">project</span> <span class="pl-e">xmlns</span>=<span class="pl-s"><span class="pl-pds">"</span>http://maven.apache.org/POM/4.0.0<span class="pl-pds">"</span></span> <span class="pl-e">xmlns</span><span class="pl-e">:</span><span class="pl-e">xsi</span>=<span class="pl-s"><span class="pl-pds">"</span>http://www.w3.org/2001/XMLSchema-instance<span class="pl-pds">"</span></span> <span class="pl-e">xsi</span><span class="pl-e">:</span><span class="pl-e">schemaLocation</span>=<span class="pl-s"><span class="pl-pds">"</span>http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd<span class="pl-pds">"</span></span>> <<span class="pl-ent">modelVersion</span>>4.0.0</<span class="pl-ent">modelVersion</span>> <<span class="pl-ent">groupId</span>>org.superbiz.demo</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>demo-crest-arthur</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>1.0-SNAPSHOT</<span class="pl-ent">version</span>> <<span class="pl-ent">properties</span>> <<span class="pl-ent">crest</span>.version>...</<span class="pl-ent">crest</span>.version> <span class="pl-c"><span class="pl-c"><!--</span> >= 0.17 <span class="pl-c">--></span></span> </<span class="pl-ent">properties</span>> <<span class="pl-ent">dependencies</span>> <<span class="pl-ent">dependency</span>> <<span class="pl-ent">groupId</span>>org.tomitribe</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>tomitribe-crest</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>${crest.vesion}</<span class="pl-ent">version</span>> </<span class="pl-ent">dependency</span>> </<span class="pl-ent">dependencies</span>> <<span class="pl-ent">build</span>> <<span class="pl-ent">plugins</span>> <<span class="pl-ent">plugin</span>> <<span class="pl-ent">groupId</span>>org.apache.maven.plugins</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>maven-resources-plugin</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>3.2.0</<span class="pl-ent">version</span>> <<span class="pl-ent">configuration</span>> <<span class="pl-ent">encoding</span>>UTF-8</<span class="pl-ent">encoding</span>> </<span class="pl-ent">configuration</span>> </<span class="pl-ent">plugin</span>> <<span class="pl-ent">plugin</span>> <<span class="pl-ent">groupId</span>>org.apache.maven.plugins</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>maven-compiler-plugin</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>3.8.1</<span class="pl-ent">version</span>> <<span class="pl-ent">configuration</span>> <<span class="pl-ent">release</span>>17</<span class="pl-ent">release</span>> <<span class="pl-ent">source</span>>17</<span class="pl-ent">source</span>> <<span class="pl-ent">target</span>>17</<span class="pl-ent">target</span>> </<span class="pl-ent">configuration</span>> </<span class="pl-ent">plugin</span>> <<span class="pl-ent">plugin</span>> <<span class="pl-ent">groupId</span>>org.apache.geronimo.arthur</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>arthur-maven-plugin</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>1.0.3</<span class="pl-ent">version</span>> <<span class="pl-ent">configuration</span>> <<span class="pl-ent">graalVersion</span>>21.3.0.r17</<span class="pl-ent">graalVersion</span>> <<span class="pl-ent">main</span>>org.tomitribe.crest.Main</<span class="pl-ent">main</span>> <<span class="pl-ent">graalExtensions</span>> <span class="pl-c"><span class="pl-c"><!--</span> enable crest auto registration for commands/interceptors <span class="pl-c">--></span></span> <<span class="pl-ent">graalExtension</span>>org.tomitribe:tomitribe-crest-arthur-extension:0.17-SNAPSHOT</<span class="pl-ent">graalExtension</span>> </<span class="pl-ent">graalExtensions</span>> <<span class="pl-ent">extensionProperties</span>> <span class="pl-c"><span class="pl-c"><!--</span> starts with, excludes exists too <span class="pl-c">--></span></span> <<span class="pl-ent">tomitribe</span>.crest.command.includes> ${project.groupId}., org.tomitribe.crest.cmds.processors.Help </<span class="pl-ent">tomitribe</span>.crest.command.includes> </<span class="pl-ent">extensionProperties</span>> <span class="pl-c"><span class="pl-c"><!--</span> this option is deprecated in recent graal versions <span class="pl-c">--></span></span> <<span class="pl-ent">enableAllSecurityServices</span>>false</<span class="pl-ent">enableAllSecurityServices</span>> </<span class="pl-ent">configuration</span>> <<span class="pl-ent">dependencies</span>> <<span class="pl-ent">dependency</span>> <span class="pl-c"><span class="pl-c"><!--</span> force arthur to support java 17 <span class="pl-c">--></span></span> <<span class="pl-ent">groupId</span>>org.apache.xbean</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>xbean-asm9-shaded</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>4.20</<span class="pl-ent">version</span>> </<span class="pl-ent">dependency</span>> </<span class="pl-ent">dependencies</span>> </<span class="pl-ent">plugin</span>> </<span class="pl-ent">plugins</span>> </<span class="pl-ent">build</span>> </<span class="pl-ent">project</span>></pre></div> </div> </div> <div dir="auto"> <p dir="auto">Once this project created, you can run <code>mvn clean install arthur:native-image</code>. This creates a <code>./target/demo-crest-arthur.graal.bin</code> binary and you can execute <code>cat</code> command using: <code>./target/demo-crest-arthur.graal.bin cat <some file></code>.</p> </div> </div> <div dir="auto"> <div class="markdown-heading" dir="auto"><h3 id="user-content-use-crest-maven-plugin-scanning" tabindex="-1" class="heading-element" dir="auto">Use Crest Maven Plugin Scanning</h3><a id="user-content-use-crest-maven-plugin-scanning" class="anchor" aria-label="Permalink: Use Crest Maven Plugin Scanning" href="#use-crest-maven-plugin-scanning"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div dir="auto"> <p dir="auto">it is also possible to make Arthur extension use <code>crest-maven-plugin</code> scan goal (<code>descriptor</code>). Just set extension property <code>tomitribe.crest.useInPlaceRegistrations</code> to <code>true</code>:</p> </div> <div dir="auto"> <div dir="auto"> <div class="highlight highlight-text-xml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="<plugin> <groupId>org.tomitribe</groupId> <artifactId>crest-maven-plugin</artifactId> <version>${crest.version}</version> <executions> <execution> <id>scan</id> <phase>process-classes</phase> <goals> <goal>descriptor</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.geronimo.arthur</groupId> <artifactId>arthur-maven-plugin</artifactId> <version>1.0.3</version> <configuration> <graalVersion>21.3.0.r17</graalVersion> <main>org.tomitribe.crest.Main</main> <graalExtensions> <graalExtension>org.tomitribe:tomitribe-crest-arthur-extension:0.17-SNAPSHOT</graalExtension> </graalExtensions> <extensionProperties> <!-- reuse crest maven plugin scanning --> <tomitribe.crest.useInPlaceRegistrations>true</tomitribe.crest.useInPlaceRegistrations> </extensionProperties> <enableAllSecurityServices>false</enableAllSecurityServices> </configuration> <dependencies> <dependency> <!-- force arthur to support java 17 --> <groupId>org.apache.xbean</groupId> <artifactId>xbean-asm9-shaded</artifactId> <version>4.20</version> </dependency> </dependencies> </plugin>"><pre><<span class="pl-ent">plugin</span>> <<span class="pl-ent">groupId</span>>org.tomitribe</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>crest-maven-plugin</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>${crest.version}</<span class="pl-ent">version</span>> <<span class="pl-ent">executions</span>> <<span class="pl-ent">execution</span>> <<span class="pl-ent">id</span>>scan</<span class="pl-ent">id</span>> <<span class="pl-ent">phase</span>>process-classes</<span class="pl-ent">phase</span>> <<span class="pl-ent">goals</span>> <<span class="pl-ent">goal</span>>descriptor</<span class="pl-ent">goal</span>> </<span class="pl-ent">goals</span>> </<span class="pl-ent">execution</span>> </<span class="pl-ent">executions</span>> </<span class="pl-ent">plugin</span>> <<span class="pl-ent">plugin</span>> <<span class="pl-ent">groupId</span>>org.apache.geronimo.arthur</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>arthur-maven-plugin</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>1.0.3</<span class="pl-ent">version</span>> <<span class="pl-ent">configuration</span>> <<span class="pl-ent">graalVersion</span>>21.3.0.r17</<span class="pl-ent">graalVersion</span>> <<span class="pl-ent">main</span>>org.tomitribe.crest.Main</<span class="pl-ent">main</span>> <<span class="pl-ent">graalExtensions</span>> <<span class="pl-ent">graalExtension</span>>org.tomitribe:tomitribe-crest-arthur-extension:0.17-SNAPSHOT</<span class="pl-ent">graalExtension</span>> </<span class="pl-ent">graalExtensions</span>> <<span class="pl-ent">extensionProperties</span>> <span class="pl-c"><span class="pl-c"><!--</span> reuse crest maven plugin scanning <span class="pl-c">--></span></span> <<span class="pl-ent">tomitribe</span>.crest.useInPlaceRegistrations>true</<span class="pl-ent">tomitribe</span>.crest.useInPlaceRegistrations> </<span class="pl-ent">extensionProperties</span>> <<span class="pl-ent">enableAllSecurityServices</span>>false</<span class="pl-ent">enableAllSecurityServices</span>> </<span class="pl-ent">configuration</span>> <<span class="pl-ent">dependencies</span>> <<span class="pl-ent">dependency</span>> <span class="pl-c"><span class="pl-c"><!--</span> force arthur to support java 17 <span class="pl-c">--></span></span> <<span class="pl-ent">groupId</span>>org.apache.xbean</<span class="pl-ent">groupId</span>> <<span class="pl-ent">artifactId</span>>xbean-asm9-shaded</<span class="pl-ent">artifactId</span>> <<span class="pl-ent">version</span>>4.20</<span class="pl-ent">version</span>> </<span class="pl-ent">dependency</span>> </<span class="pl-ent">dependencies</span>> </<span class="pl-ent">plugin</span>></pre></div> </div> </div> <div dir="auto"> <p dir="auto">This enables to use the same scanning for both tasks and therefore to have a common and unified scanning for java and native runs.</p> </div> </div> </div> </div></article></div></div></div></div></div> <!-- --> <!-- --> <script type="application/json" id="__PRIMER_DATA_:R0:__">{"resolvedServerColorMode":"day"}</script></div> </react-partial> <input type="hidden" data-csrf="true" value="gltE9R9aBvA+EJzFdWGM//6TdDNx8q0pnhh8yoxMUIQxUOyjdxLjVXiX1rzDiPn4TdKN40g9RckJMcbtx539qw==" /> </div> <div data-view-component="true" class="Layout-sidebar"> <div class="BorderGrid about-margin" data-pjax> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <div class="hide-sm hide-md"> <h2 class="mb-3 h4">About</h2> <p class="f4 my-3"> Command-line API modeled after JAX-RS (Command REST) </p> <h3 class="sr-only">Resources</h3> <div class="mt-2"> <a class="Link--muted" data-analytics-event="{"category":"Repository Overview","action":"click","label":"location:sidebar;file:readme"}" href="#readme-ov-file"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book mr-2"> <path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path> </svg> Readme </a> </div> <h3 class="sr-only">License</h3> <div class="mt-2"> <a href="#Apache-2.0-1-ov-file" class="Link--muted" data-analytics-event="{"category":"Repository Overview","action":"click","label":"location:sidebar;file:license"}" > <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-law mr-2"> <path d="M8.75.75V2h.985c.304 0 .603.08.867.231l1.29.736c.038.022.08.033.124.033h2.234a.75.75 0 0 1 0 1.5h-.427l2.111 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.006.005-.01.01-.045.04c-.21.176-.441.327-.686.45C14.556 10.78 13.88 11 13 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L12.178 4.5h-.162c-.305 0-.604-.079-.868-.231l-1.29-.736a.245.245 0 0 0-.124-.033H8.75V13h2.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.5V3.5h-.984a.245.245 0 0 0-.124.033l-1.289.737c-.265.15-.564.23-.869.23h-.162l2.112 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.016.015-.045.04c-.21.176-.441.327-.686.45C4.556 10.78 3.88 11 3 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L2.178 4.5H1.75a.75.75 0 0 1 0-1.5h2.234a.249.249 0 0 0 .125-.033l1.288-.737c.265-.15.564-.23.869-.23h.984V.75a.75.75 0 0 1 1.5 0Zm2.945 8.477c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327Zm-10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327Z"></path> </svg> Apache-2.0 license </a> </div> <include-fragment src="/tomitribe/crest/hovercards/citation/sidebar_partial?tree_name=master"> </include-fragment> <div class="mt-2"> <a href="/tomitribe/crest/activity" data-view-component="true" class="Link Link--muted"><svg text="gray" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-pulse mr-2"> <path d="M6 2c.306 0 .582.187.696.471L10 10.731l1.304-3.26A.751.751 0 0 1 12 7h3.25a.75.75 0 0 1 0 1.5h-2.742l-1.812 4.528a.751.751 0 0 1-1.392 0L6 4.77 4.696 8.03A.75.75 0 0 1 4 8.5H.75a.75.75 0 0 1 0-1.5h2.742l1.812-4.529A.751.751 0 0 1 6 2Z"></path> </svg> <span class="color-fg-muted">Activity</span></a> </div> <div class="mt-2"> <a href="/tomitribe/crest/custom-properties" data-view-component="true" class="Link Link--muted"><svg text="gray" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-note mr-2"> <path d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25ZM3.5 6.25a.75.75 0 0 1 .75-.75h7a.75.75 0 0 1 0 1.5h-7a.75.75 0 0 1-.75-.75Zm.75 2.25h4a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1 0-1.5Z"></path> </svg> <span class="color-fg-muted">Custom properties</span></a> </div> <h3 class="sr-only">Stars</h3> <div class="mt-2"> <a href="/tomitribe/crest/stargazers" data-view-component="true" class="Link Link--muted"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star mr-2"> <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path> </svg> <strong>57</strong> stars</a> </div> <h3 class="sr-only">Watchers</h3> <div class="mt-2"> <a href="/tomitribe/crest/watchers" data-view-component="true" class="Link Link--muted"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-eye mr-2"> <path d="M8 2c1.981 0 3.671.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.62 1.62 0 0 1 0 1.798c-.45.678-1.367 1.932-2.637 3.023C11.67 13.008 9.981 14 8 14c-1.981 0-3.671-.992-4.933-2.078C1.797 10.83.88 9.576.43 8.898a1.62 1.62 0 0 1 0-1.798c.45-.677 1.367-1.931 2.637-3.022C4.33 2.992 6.019 2 8 2ZM1.679 7.932a.12.12 0 0 0 0 .136c.411.622 1.241 1.75 2.366 2.717C5.176 11.758 6.527 12.5 8 12.5c1.473 0 2.825-.742 3.955-1.715 1.124-.967 1.954-2.096 2.366-2.717a.12.12 0 0 0 0-.136c-.412-.621-1.242-1.75-2.366-2.717C10.824 4.242 9.473 3.5 8 3.5c-1.473 0-2.825.742-3.955 1.715-1.124.967-1.954 2.096-2.366 2.717ZM8 10a2 2 0 1 1-.001-3.999A2 2 0 0 1 8 10Z"></path> </svg> <strong>21</strong> watching</a> </div> <h3 class="sr-only">Forks</h3> <div class="mt-2"> <a href="/tomitribe/crest/forks" data-view-component="true" class="Link Link--muted"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo-forked mr-2"> <path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"></path> </svg> <strong>18</strong> forks</a> </div> <div class="mt-2"> <a class="Link--muted" href="/contact/report-content?content_url=https%3A%2F%2Fgithub.com%2Ftomitribe%2Fcrest&report=tomitribe+%28user%29"> Report repository </a> </div> </div> </div> </div> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <h2 class="h4 mb-3" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame"> <a href="/tomitribe/crest/releases" data-view-component="true" class="Link--primary no-underline Link">Releases</a></h2> <a class="Link--primary no-underline" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" href="/tomitribe/crest/tags"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-tag"> <path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path> </svg> <span class="text-bold">36</span> <span class="color-fg-muted">tags</span> </a> </div> </div> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <h2 class="h4 mb-3"> <a href="/orgs/tomitribe/packages?repo_name=crest" data-view-component="true" class="Link--primary no-underline Link d-flex flex-items-center">Packages <span title="0" hidden="hidden" data-view-component="true" class="Counter ml-1">0</span></a></h2> <div class="text-small color-fg-muted" > No packages published <br> </div> </div> </div> <div class="BorderGrid-row" hidden> <div class="BorderGrid-cell"> <include-fragment src="/tomitribe/crest/used_by_list" accept="text/fragment+html"> </include-fragment> </div> </div> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <h2 class="h4 mb-3"> <a href="/tomitribe/crest/graphs/contributors" data-view-component="true" class="Link--primary no-underline Link d-flex flex-items-center">Contributors <span title="12" data-view-component="true" class="Counter ml-1">12</span></a></h2> <include-fragment src="/tomitribe/crest/contributors_list?count=12&current_repository=crest&items_to_show=12" aria-busy="true" aria-label="Loading contributors"> <ul class="list-style-none d-flex flex-wrap mb-n2"> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> <li class="mb-2 "> <div class="Skeleton avatar avatar-user mr-2" style="width:32px;height:32px;"></div> </li> </ul> </include-fragment> </div> </div> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <h2 class="h4 mb-3">Languages</h2> <div class="mb-2"> <span data-view-component="true" class="Progress"> <span style="background-color:#b07219 !important;;width: 98.5%;" itemprop="keywords" data-view-component="true" class="Progress-item color-bg-success-emphasis"></span> <span style="background-color:#89e051 !important;;width: 1.1%;" itemprop="keywords" data-view-component="true" class="Progress-item color-bg-success-emphasis"></span> <span style="background-color:#4298b8 !important;;width: 0.4%;" itemprop="keywords" data-view-component="true" class="Progress-item color-bg-success-emphasis"></span> </span></div> <ul class="list-style-none"> <li class="d-inline"> <a class="d-inline-flex flex-items-center flex-nowrap Link--secondary no-underline text-small mr-3" href="/tomitribe/crest/search?l=java" data-ga-click="Repository, language stats search click, location:repo overview"> <svg style="color:#b07219;" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-dot-fill mr-2"> <path d="M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z"></path> </svg> <span class="color-fg-default text-bold mr-1">Java</span> <span>98.5%</span> </a> </li> <li class="d-inline"> <a class="d-inline-flex flex-items-center flex-nowrap Link--secondary no-underline text-small mr-3" href="/tomitribe/crest/search?l=shell" data-ga-click="Repository, language stats search click, location:repo overview"> <svg style="color:#89e051;" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-dot-fill mr-2"> <path d="M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z"></path> </svg> <span class="color-fg-default text-bold mr-1">Shell</span> <span>1.1%</span> </a> </li> <li class="d-inline"> <a class="d-inline-flex flex-items-center flex-nowrap Link--secondary no-underline text-small mr-3" href="/tomitribe/crest/search?l=groovy" data-ga-click="Repository, language stats search click, location:repo overview"> <svg style="color:#4298b8;" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-dot-fill mr-2"> <path d="M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z"></path> </svg> <span class="color-fg-default text-bold mr-1">Groovy</span> <span>0.4%</span> </a> </li> </ul> </div> </div> </div> </div> </div></div> </div> </div> </turbo-frame> </main> </div> </div> <footer class="footer pt-8 pb-6 f6 color-fg-muted p-responsive" role="contentinfo" > <h2 class='sr-only'>Footer</h2> <div class="d-flex flex-justify-center flex-items-center flex-column-reverse flex-lg-row flex-wrap flex-lg-nowrap"> <div class="d-flex flex-items-center flex-shrink-0 mx-2"> <a aria-label="Homepage" title="GitHub" class="footer-octicon mr-2" href="https://github.com"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-mark-github"> <path d="M12 1C5.9225 1 1 5.9225 1 12C1 16.8675 4.14875 20.9787 8.52125 22.4362C9.07125 22.5325 9.2775 22.2025 9.2775 21.9137C9.2775 21.6525 9.26375 20.7862 9.26375 19.865C6.5 20.3737 5.785 19.1912 5.565 18.5725C5.44125 18.2562 4.905 17.28 4.4375 17.0187C4.0525 16.8125 3.5025 16.3037 4.42375 16.29C5.29 16.2762 5.90875 17.0875 6.115 17.4175C7.105 19.0812 8.68625 18.6137 9.31875 18.325C9.415 17.61 9.70375 17.1287 10.02 16.8537C7.5725 16.5787 5.015 15.63 5.015 11.4225C5.015 10.2262 5.44125 9.23625 6.1425 8.46625C6.0325 8.19125 5.6475 7.06375 6.2525 5.55125C6.2525 5.55125 7.17375 5.2625 9.2775 6.67875C10.1575 6.43125 11.0925 6.3075 12.0275 6.3075C12.9625 6.3075 13.8975 6.43125 14.7775 6.67875C16.8813 5.24875 17.8025 5.55125 17.8025 5.55125C18.4075 7.06375 18.0225 8.19125 17.9125 8.46625C18.6138 9.23625 19.04 10.2125 19.04 11.4225C19.04 15.6437 16.4688 16.5787 14.0213 16.8537C14.42 17.1975 14.7638 17.8575 14.7638 18.8887C14.7638 20.36 14.75 21.5425 14.75 21.9137C14.75 22.2025 14.9563 22.5462 15.5063 22.4362C19.8513 20.9787 23 16.8537 23 12C23 5.9225 18.0775 1 12 1Z"></path> </svg> </a> <span> © 2025 GitHub, Inc. </span> </div> <nav aria-label="Footer"> <h3 class="sr-only" id="sr-footer-heading">Footer navigation</h3> <ul class="list-style-none d-flex flex-justify-center flex-wrap mb-2 mb-lg-0" aria-labelledby="sr-footer-heading"> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to Terms","label":"text:terms"}" href="https://docs.github.com/site-policy/github-terms/github-terms-of-service" data-view-component="true" class="Link--secondary Link">Terms</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to privacy","label":"text:privacy"}" href="https://docs.github.com/site-policy/privacy-policies/github-privacy-statement" data-view-component="true" class="Link--secondary Link">Privacy</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to security","label":"text:security"}" href="https://github.com/security" data-view-component="true" class="Link--secondary Link">Security</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to status","label":"text:status"}" href="https://www.githubstatus.com/" data-view-component="true" class="Link--secondary Link">Status</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to docs","label":"text:docs"}" href="https://docs.github.com/" data-view-component="true" class="Link--secondary Link">Docs</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to contact","label":"text:contact"}" href="https://support.github.com?tags=dotcom-footer" data-view-component="true" class="Link--secondary Link">Contact</a> </li> <li class="mx-2" > <cookie-consent-link> <button type="button" class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent" data-action="click:cookie-consent-link#showConsentManagement" data-analytics-event="{"location":"footer","action":"cookies","context":"subfooter","tag":"link","label":"cookies_link_subfooter_footer"}" > Manage cookies </button> </cookie-consent-link> </li> <li class="mx-2"> <cookie-consent-link> <button type="button" class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent" data-action="click:cookie-consent-link#showConsentManagement" data-analytics-event="{"location":"footer","action":"dont_share_info","context":"subfooter","tag":"link","label":"dont_share_info_link_subfooter_footer"}" > Do not share my personal information </button> </cookie-consent-link> </li> </ul> </nav> </div> </footer> <ghcc-consent id="ghcc" class="position-fixed bottom-0 left-0" style="z-index: 999999" data-initial-cookie-consent-allowed="" data-cookie-consent-required="false"></ghcc-consent> <div id="ajax-error-message" class="ajax-error-message flash flash-error" hidden> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert"> <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> <button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg> </button> You can’t perform that action at this time. </div> <template id="site-details-dialog"> <details class="details-reset details-overlay details-overlay-dark lh-default color-fg-default hx_rsm" open> <summary role="button" aria-label="Close dialog"></summary> <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal"> <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg> </button> <div class="octocat-spinner my-6 js-details-dialog-spinner"></div> </details-dialog> </details> </template> <div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;"> <div class="Popover-message Popover-message--bottom-left Popover-message--large Box color-shadow-large" style="width:360px;"> </div> </div> <template id="snippet-clipboard-copy-button"> <div class="zeroclipboard-container position-absolute right-0 top-0"> <clipboard-copy aria-label="Copy" class="ClipboardButton btn js-clipboard-copy m-2 p-0" data-copy-feedback="Copied!" data-tooltip-direction="w"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon m-2"> <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> </svg> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none m-2"> <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path> </svg> </clipboard-copy> </div> </template> <template id="snippet-clipboard-copy-button-unpositioned"> <div class="zeroclipboard-container"> <clipboard-copy aria-label="Copy" class="ClipboardButton btn btn-invisible js-clipboard-copy m-2 p-0 d-flex flex-justify-center flex-items-center" data-copy-feedback="Copied!" data-tooltip-direction="w"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon"> <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> </svg> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none"> <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path> </svg> </clipboard-copy> </div> </template> </div> <div id="js-global-screen-reader-notice" class="sr-only mt-n1" aria-live="polite" aria-atomic="true" ></div> <div id="js-global-screen-reader-notice-assertive" class="sr-only mt-n1" aria-live="assertive" aria-atomic="true"></div> </body> </html>