-
Notifications
You must be signed in to change notification settings - Fork 0
feat(refactor): add core library foundation with internal abstractions [1 of 8] #273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| [package] | ||
| name = "dev-scope" | ||
| name = "dx-scope" | ||
| version = "0.0.0-dev" | ||
| edition = "2024" | ||
| default-run = "scope" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| use clap::Parser; | ||
| use dev_scope::prelude::*; | ||
| use dx_scope::prelude::*; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2 questions
|
||
| use human_panic::setup_panic; | ||
| use std::env; | ||
| use std::sync::Arc; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| //! CLI-specific implementations for interactive usage. | ||
| //! | ||
| //! This module provides implementations of the library traits that are | ||
| //! suitable for command-line interface usage, including: | ||
| //! | ||
| //! - Interactive prompts using the `inquire` crate | ||
| //! - Visual progress reporting using `tracing-indicatif` | ||
| //! | ||
| //! # When to Use | ||
| //! | ||
| //! Use the implementations in this module when building CLI applications | ||
| //! that need interactive user prompts. For library usage, automated | ||
| //! environments, or testing, use the implementations in [`crate::internal`]. | ||
| //! | ||
| //! # Examples | ||
| //! | ||
| //! ## Interactive CLI Application | ||
| //! | ||
| //! ```rust | ||
| //! use dx_scope::InquireInteraction; | ||
| //! use dx_scope::internal::prompts::UserInteraction; | ||
| //! | ||
| //! let interaction = InquireInteraction; | ||
| //! | ||
| //! // This will show an interactive prompt in the terminal | ||
| //! // In non-TTY environments (like this doc test), it returns false | ||
| //! let result = interaction.confirm("Apply this fix?", Some("This will modify files")); | ||
| //! // result is false in doc test environment (no TTY) | ||
| //! ``` | ||
| //! | ||
| //! # TTY Detection | ||
| //! | ||
| //! `InquireInteraction` automatically detects when stdin is not a TTY | ||
| //! (e.g., when running in a pipe or CI environment) and returns `false` | ||
| //! instead of crashing. For explicit control in non-interactive environments, | ||
| //! use [`AutoApprove`](crate::AutoApprove) or [`DenyAll`](crate::DenyAll). | ||
|
|
||
| use crate::internal::prompts::UserInteraction; | ||
| use inquire::InquireError; | ||
| use tracing::warn; | ||
|
|
||
| /// CLI user interaction using the `inquire` crate. | ||
| /// | ||
| /// This implementation provides interactive prompts suitable for terminal usage. | ||
| /// It handles TTY detection and gracefully falls back to denial when running | ||
| /// in non-interactive environments. | ||
| /// | ||
| /// # Example | ||
| /// | ||
| /// ```rust | ||
| /// use dx_scope::InquireInteraction; | ||
| /// use dx_scope::internal::prompts::UserInteraction; | ||
| /// | ||
| /// let interaction = InquireInteraction; | ||
| /// // In non-TTY environments, confirm() returns false gracefully | ||
| /// let confirmed = interaction.confirm("Apply fix?", Some("This will modify files")); | ||
| /// ``` | ||
| #[derive(Debug, Clone, Copy, Default)] | ||
| pub struct InquireInteraction; | ||
|
|
||
| impl UserInteraction for InquireInteraction { | ||
| fn confirm(&self, prompt: &str, help_text: Option<&str>) -> bool { | ||
| tracing_indicatif::suspend_tracing_indicatif(|| { | ||
| let base_prompt = inquire::Confirm::new(prompt).with_default(false); | ||
| let prompt = match help_text { | ||
| Some(text) => base_prompt.with_help_message(text), | ||
| None => base_prompt, | ||
| }; | ||
|
|
||
| match prompt.prompt() { | ||
| Ok(result) => result, | ||
| Err(InquireError::NotTTY) => { | ||
| warn!(target: "user", "Prompting user, but input device is not a TTY. Skipping."); | ||
| false | ||
| } | ||
| Err(_) => false, | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| fn notify(&self, message: &str) { | ||
| println!("{}", message); | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn test_inquire_interaction_is_send_sync() { | ||
| fn assert_send_sync<T: Send + Sync>() {} | ||
| assert_send_sync::<InquireInteraction>(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test appears to be testing the compiler's type system. |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| //! Internal abstractions for library-first design. | ||
| //! | ||
| //! This module contains traits and implementations that allow the scope library | ||
| //! to be used both as a CLI tool and as a programmatic library. These abstractions | ||
| //! decouple the core logic from specific UI implementations. | ||
| //! | ||
| //! # Overview | ||
| //! | ||
| //! The internal module provides: | ||
| //! - [`prompts`] - User interaction abstractions (confirmations, notifications) | ||
| //! - [`progress`] - Progress reporting abstractions | ||
| //! | ||
| //! # Choosing an Implementation | ||
| //! | ||
| //! | Use Case | UserInteraction | ProgressReporter | | ||
| //! |----------|-----------------|------------------| | ||
| //! | CLI/Interactive | `InquireInteraction` | (use tracing-indicatif) | | ||
| //! | CI/Automated | `AutoApprove` | `NoOpProgress` | | ||
| //! | Dry-run/Testing | `DenyAll` | `NoOpProgress` | | ||
| //! | Custom | Implement trait | Implement trait | | ||
| //! | ||
| //! # Examples | ||
| //! | ||
| //! ## Automated Environment (CI) | ||
| //! | ||
| //! ```rust | ||
| //! use dx_scope::internal::prompts::{UserInteraction, AutoApprove}; | ||
| //! use dx_scope::internal::progress::{ProgressReporter, NoOpProgress}; | ||
| //! | ||
| //! let interaction = AutoApprove; | ||
| //! let progress = NoOpProgress; | ||
| //! | ||
| //! // All prompts will be automatically approved | ||
| //! assert!(interaction.confirm("Apply fix?", None)); | ||
| //! | ||
| //! // Progress calls are silent | ||
| //! progress.start_group("build", 5); | ||
| //! progress.finish_group(); | ||
| //! ``` | ||
| //! | ||
| //! ## Dry-Run Mode | ||
| //! | ||
| //! ```rust | ||
| //! use dx_scope::internal::prompts::{UserInteraction, DenyAll}; | ||
| //! | ||
| //! let interaction = DenyAll; | ||
| //! | ||
| //! // All prompts will be denied - no changes made | ||
| //! assert!(!interaction.confirm("Apply fix?", None)); | ||
| //! ``` | ||
| //! | ||
| //! ## Custom Implementation | ||
| //! | ||
| //! ```rust | ||
| //! use dx_scope::internal::prompts::UserInteraction; | ||
| //! | ||
| //! struct AlwaysAskUser; | ||
| //! | ||
| //! impl UserInteraction for AlwaysAskUser { | ||
| //! fn confirm(&self, prompt: &str, _help: Option<&str>) -> bool { | ||
| //! // Custom logic - maybe read from a config file | ||
| //! println!("Would prompt: {}", prompt); | ||
| //! false | ||
| //! } | ||
| //! | ||
| //! fn notify(&self, message: &str) { | ||
| //! println!("[INFO] {}", message); | ||
| //! } | ||
| //! } | ||
| //! ``` | ||
| //! | ||
| //! # Thread Safety | ||
| //! | ||
| //! All provided implementations are `Send + Sync`, making them safe to use | ||
| //! across async tasks and threads. | ||
|
|
||
| pub mod progress; | ||
| pub mod prompts; | ||
|
|
||
| // Re-export commonly used types at the module level | ||
| pub use progress::{NoOpProgress, ProgressReporter}; | ||
| pub use prompts::{AutoApprove, DenyAll, UserInteraction}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As part of this, we should stop reexporting everything all the time. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good comment. Says why not what. Double plus good.