diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-26 20:04:02 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-26 20:04:02 +0900 |
| commit | f411daceacad66e0bd774fda7d3c5ef8533cc55c (patch) | |
| tree | eefb065e4d676a3f7031ca49bab21c773b00b134 /crates/shirabe/src/io | |
| parent | 1921f173ea219cb4b25847294d2d3fa465550fbb (diff) | |
| download | php-shirabe-f411daceacad66e0bd774fda7d3c5ef8533cc55c.tar.gz php-shirabe-f411daceacad66e0bd774fda7d3c5ef8533cc55c.tar.zst php-shirabe-f411daceacad66e0bd774fda7d3c5ef8533cc55c.zip | |
refactor(io): share IOInterface via Rc<RefCell<dyn _>> handle
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/io')
| -rw-r--r-- | crates/shirabe/src/io/buffer_io.rs | 52 | ||||
| -rw-r--r-- | crates/shirabe/src/io/console_io.rs | 63 | ||||
| -rw-r--r-- | crates/shirabe/src/io/io_interface.rs | 215 | ||||
| -rw-r--r-- | crates/shirabe/src/io/null_io.rs | 59 |
4 files changed, 268 insertions, 121 deletions
diff --git a/crates/shirabe/src/io/buffer_io.rs b/crates/shirabe/src/io/buffer_io.rs index 867eb0e..ed87b71 100644 --- a/crates/shirabe/src/io/buffer_io.rs +++ b/crates/shirabe/src/io/buffer_io.rs @@ -129,39 +129,9 @@ impl BufferIO { } } -// TODO(phase-b): PHP `class BufferIO extends ConsoleIO` — delegate all IOInterface, -// LoggerInterface, and BaseIO methods to `self.inner` (ConsoleIO). -impl shirabe_external_packages::psr::log::LoggerInterface for BufferIO { - fn emergency(&self, message: &str, context: &[(&str, &str)]) { - self.inner.emergency(message, context) - } - fn alert(&self, message: &str, context: &[(&str, &str)]) { - self.inner.alert(message, context) - } - fn critical(&self, message: &str, context: &[(&str, &str)]) { - self.inner.critical(message, context) - } - fn error(&self, message: &str, context: &[(&str, &str)]) { - self.inner.error(message, context) - } - fn warning(&self, message: &str, context: &[(&str, &str)]) { - self.inner.warning(message, context) - } - fn notice(&self, message: &str, context: &[(&str, &str)]) { - self.inner.notice(message, context) - } - fn info(&self, message: &str, context: &[(&str, &str)]) { - self.inner.info(message, context) - } - fn debug(&self, message: &str, context: &[(&str, &str)]) { - self.inner.debug(message, context) - } - fn log(&self, level: &str, message: &str, context: &[(&str, &str)]) { - self.inner.log(level, message, context) - } -} - -impl crate::io::IOInterface for BufferIO { +// TODO(phase-b): PHP `class BufferIO extends ConsoleIO` — delegate all +// IOInterface and BaseIO methods to `self.inner` (ConsoleIO). +impl crate::io::IOInterfaceImmutable for BufferIO { fn is_interactive(&self) -> bool { self.inner.is_interactive() } @@ -247,6 +217,20 @@ impl crate::io::IOInterface for BufferIO { ) -> indexmap::IndexMap<String, Option<String>> { self.inner.get_authentication(repository_name) } + fn error(&self, message: &str, context: &[(&str, &str)]) { + self.inner.error(message, context) + } + + fn warning(&self, message: &str, context: &[(&str, &str)]) { + self.inner.warning(message, context) + } + + fn debug(&self, message: &str, context: &[(&str, &str)]) { + self.inner.debug(message, context) + } +} + +impl crate::io::IOInterfaceMutable for BufferIO { fn set_authentication( &mut self, repository_name: String, @@ -261,6 +245,8 @@ impl crate::io::IOInterface for BufferIO { } } +impl crate::io::IOInterface for BufferIO {} + impl crate::io::BaseIO for BufferIO { fn authentications( &self, diff --git a/crates/shirabe/src/io/console_io.rs b/crates/shirabe/src/io/console_io.rs index b4bae47..2b1c334 100644 --- a/crates/shirabe/src/io/console_io.rs +++ b/crates/shirabe/src/io/console_io.rs @@ -5,7 +5,6 @@ use crate::io::io_interface; use indexmap::IndexMap; use indexmap::indexmap; use shirabe_external_packages::composer::pcre::Preg; -use shirabe_external_packages::psr::log::LoggerInterface; use shirabe_external_packages::symfony::component::console::helper::HelperSet; use shirabe_external_packages::symfony::component::console::helper::ProgressBar; use shirabe_external_packages::symfony::component::console::helper::Table; @@ -25,6 +24,8 @@ use std::cell::RefCell; use crate::io::BaseIO; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; +use crate::io::IOInterfaceMutable; use crate::question::StrictConfirmationQuestion; use crate::util::Silencer; @@ -378,49 +379,7 @@ impl ConsoleIO { } } -impl LoggerInterface for ConsoleIO { - // TODO(phase-b): BaseIO's emergency/alert/.../log take PhpMixed and - // IndexMap<String, Box<PhpMixed>> while LoggerInterface takes &str and - // &[(&str, &str)]. Delegation requires reconciling signatures; for now, - // mirror NullIO and panic via todo!(). - fn emergency(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn alert(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn critical(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn error(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn warning(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn notice(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn info(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn debug(&self, _message: &str, _context: &[(&str, &str)]) { - todo!() - } - - fn log(&self, _level: &str, _message: &str, _context: &[(&str, &str)]) { - todo!() - } -} - -impl IOInterface for ConsoleIO { +impl IOInterfaceImmutable for ConsoleIO { fn is_interactive(&self) -> bool { self.input.is_interactive() } @@ -690,6 +649,20 @@ impl IOInterface for ConsoleIO { <Self as BaseIO>::get_authentication(self, repository_name) } + fn error(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } + + fn warning(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } + + fn debug(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } +} + +impl IOInterfaceMutable for ConsoleIO { fn set_authentication( &mut self, repository_name: String, @@ -704,6 +677,8 @@ impl IOInterface for ConsoleIO { } } +impl IOInterface for ConsoleIO {} + impl BaseIO for ConsoleIO { fn authentications( &self, diff --git a/crates/shirabe/src/io/io_interface.rs b/crates/shirabe/src/io/io_interface.rs index 1d97700..aac1488 100644 --- a/crates/shirabe/src/io/io_interface.rs +++ b/crates/shirabe/src/io/io_interface.rs @@ -2,8 +2,9 @@ use crate::config::Config; use indexmap::IndexMap; -use shirabe_external_packages::psr::log::LoggerInterface; use shirabe_php_shim::PhpMixed; +use std::cell::RefCell; +use std::rc::Rc; pub const QUIET: i64 = 1; pub const NORMAL: i64 = 2; @@ -11,7 +12,18 @@ pub const VERBOSE: i64 = 4; pub const VERY_VERBOSE: i64 = 8; pub const DEBUG: i64 = 16; -pub trait IOInterface: LoggerInterface + std::fmt::Debug { +// In PHP this is `IOInterface extends LoggerInterface`. Shirabe does not +// integrate with the PHP runtime, so there is no need for a separate +// `LoggerInterface` entity on the Rust side: the LoggerInterface methods that +// Composer actually invokes through an IO are folded directly into this trait +// (each is annotated as originating from LoggerInterface). +// +// On the Rust side the interface is split into an immutable part (`&self` +// methods, below) and a mutable part (`IOInterfaceMutable`, the `&mut self` +// methods). The shared handle `Rc<RefCell<dyn IOInterface>>` implements only +// `IOInterfaceImmutable`, so the mutating methods are reachable only via +// `io.borrow_mut()` — enforced at compile time rather than at runtime. +pub trait IOInterfaceImmutable: std::fmt::Debug { fn is_interactive(&self) -> bool; fn is_verbose(&self) -> bool; @@ -112,6 +124,19 @@ pub trait IOInterface: LoggerInterface + std::fmt::Debug { fn get_authentication(&self, repository_name: &str) -> IndexMap<String, Option<String>>; + // From PHP `LoggerInterface` (which `IOInterface` extends). Only the + // variants Composer actually calls through an IO are kept. + fn error(&self, message: &str, context: &[(&str, &str)]); + + fn warning(&self, message: &str, context: &[(&str, &str)]); + + fn debug(&self, message: &str, context: &[(&str, &str)]); +} + +// The `&mut self` part of PHP `IOInterface`. The shared handle does NOT +// implement this trait, so these methods can only be reached through +// `io.borrow_mut()`. +pub trait IOInterfaceMutable { fn set_authentication( &mut self, repository_name: String, @@ -120,8 +145,190 @@ pub trait IOInterface: LoggerInterface + std::fmt::Debug { ); fn load_configuration(&mut self, config: &mut Config) -> anyhow::Result<()>; +} + +// PHP `IOInterface`. This is the type used for the shared trait object +// `dyn IOInterface`; its vtable carries both the immutable and mutable methods. +pub trait IOInterface: IOInterfaceImmutable + IOInterfaceMutable {} + +// Shared-ownership handle for a PHP IO instance (reference semantics). It +// exposes only the immutable surface; mutating methods (`set_authentication`, +// `load_configuration`) are reached via `io.borrow_mut()`. Because the handle +// does not implement `IOInterfaceMutable`, calling those directly on the handle +// is a compile error rather than a runtime panic. +impl IOInterfaceImmutable for Rc<RefCell<dyn IOInterface>> { + fn is_interactive(&self) -> bool { + self.borrow().is_interactive() + } + + fn is_verbose(&self) -> bool { + self.borrow().is_verbose() + } + + fn is_very_verbose(&self) -> bool { + self.borrow().is_very_verbose() + } + + fn is_debug(&self) -> bool { + self.borrow().is_debug() + } + + fn is_decorated(&self) -> bool { + self.borrow().is_decorated() + } + + fn write(&self, message: &str) { + self.borrow().write(message) + } + + fn write2(&self, message: &str, newline: bool) { + self.borrow().write2(message, newline) + } + + fn write_no_newline(&self, message: &str) { + self.borrow().write_no_newline(message) + } + + fn write3(&self, message: &str, newline: bool, verbosity: i64) { + self.borrow().write3(message, newline, verbosity) + } + + fn write_error(&self, message: &str) { + self.borrow().write_error(message) + } + + fn write_error2(&self, message: &str, newline: bool) { + self.borrow().write_error2(message, newline) + } + + fn write_error_no_newline(&self, message: &str) { + self.borrow().write_error_no_newline(message) + } + + fn write_error3(&self, message: &str, newline: bool, verbosity: i64) { + self.borrow().write_error3(message, newline, verbosity) + } + + fn write_raw(&self, message: &str) { + self.borrow().write_raw(message) + } + + fn write_raw2(&self, message: &str, newline: bool) { + self.borrow().write_raw2(message, newline) + } + + fn write_raw3(&self, message: &str, newline: bool, verbosity: i64) { + self.borrow().write_raw3(message, newline, verbosity) + } + + fn write_error_raw(&self, message: &str) { + self.borrow().write_error_raw(message) + } + + fn write_error_raw2(&self, message: &str, newline: bool) { + self.borrow().write_error_raw2(message, newline) + } + + fn write_error_raw3(&self, message: &str, newline: bool, verbosity: i64) { + self.borrow().write_error_raw3(message, newline, verbosity) + } + + fn overwrite(&self, message: &str) { + self.borrow().overwrite(message) + } + + fn overwrite2(&self, message: &str, newline: bool) { + self.borrow().overwrite2(message, newline) + } + + fn overwrite3(&self, message: &str, newline: bool, size: Option<i64>) { + self.borrow().overwrite3(message, newline, size) + } + + fn overwrite4(&self, message: &str, newline: bool, size: Option<i64>, verbosity: i64) { + self.borrow().overwrite4(message, newline, size, verbosity) + } + + fn overwrite_error(&self, message: &str) { + self.borrow().overwrite_error(message) + } + + fn overwrite_error2(&self, message: &str, newline: bool) { + self.borrow().overwrite_error2(message, newline) + } + + fn overwrite_error3(&self, message: &str, newline: bool, size: Option<i64>) { + self.borrow().overwrite_error3(message, newline, size) + } + + fn overwrite_error4(&self, message: &str, newline: bool, size: Option<i64>, verbosity: i64) { + self.borrow() + .overwrite_error4(message, newline, size, verbosity) + } + + fn ask(&self, question: String, default: PhpMixed) -> PhpMixed { + self.borrow().ask(question, default) + } + + fn ask_confirmation(&self, question: String, default: bool) -> bool { + self.borrow().ask_confirmation(question, default) + } + + fn ask_and_validate( + &self, + question: String, + validator: Box<dyn Fn(PhpMixed) -> PhpMixed>, + attempts: Option<i64>, + default: PhpMixed, + ) -> PhpMixed { + self.borrow() + .ask_and_validate(question, validator, attempts, default) + } + + fn ask_and_hide_answer(&self, question: String) -> Option<String> { + self.borrow().ask_and_hide_answer(question) + } + + fn select( + &self, + question: String, + choices: Vec<String>, + default: PhpMixed, + attempts: PhpMixed, + error_message: String, + multiselect: bool, + ) -> PhpMixed { + self.borrow().select( + question, + choices, + default, + attempts, + error_message, + multiselect, + ) + } + + fn get_authentications(&self) -> IndexMap<String, IndexMap<String, Option<String>>> { + self.borrow().get_authentications() + } + + fn has_authentication(&self, repository_name: &str) -> bool { + self.borrow().has_authentication(repository_name) + } + + fn get_authentication(&self, repository_name: &str) -> IndexMap<String, Option<String>> { + self.borrow().get_authentication(repository_name) + } + + fn error(&self, message: &str, context: &[(&str, &str)]) { + self.borrow().error(message, context) + } + + fn warning(&self, message: &str, context: &[(&str, &str)]) { + self.borrow().warning(message, context) + } - fn clone_box(&self) -> Box<dyn IOInterface> { - todo!() + fn debug(&self, message: &str, context: &[(&str, &str)]) { + self.borrow().debug(message, context) } } diff --git a/crates/shirabe/src/io/null_io.rs b/crates/shirabe/src/io/null_io.rs index e3cfdcf..b0e11f1 100644 --- a/crates/shirabe/src/io/null_io.rs +++ b/crates/shirabe/src/io/null_io.rs @@ -2,7 +2,8 @@ use crate::io::BaseIO; use crate::io::IOInterface; -use shirabe_external_packages::psr::log::LoggerInterface; +use crate::io::IOInterfaceImmutable; +use crate::io::IOInterfaceMutable; use shirabe_php_shim::PhpMixed; #[derive(Debug)] @@ -18,7 +19,7 @@ impl NullIO { } } -impl IOInterface for NullIO { +impl IOInterfaceImmutable for NullIO { fn is_interactive(&self) -> bool { false } @@ -109,6 +110,20 @@ impl IOInterface for NullIO { <Self as BaseIO>::get_authentication(self, repository_name) } + fn error(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } + + fn warning(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } + + fn debug(&self, _message: &str, _context: &[(&str, &str)]) { + todo!() + } +} + +impl IOInterfaceMutable for NullIO { fn set_authentication( &mut self, repository_name: String, @@ -123,6 +138,8 @@ impl IOInterface for NullIO { } } +impl IOInterface for NullIO {} + impl BaseIO for NullIO { fn authentications( &self, @@ -136,41 +153,3 @@ impl BaseIO for NullIO { &mut self.authentications } } - -impl LoggerInterface for NullIO { - fn emergency(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn alert(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn critical(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn error(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn warning(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn notice(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn info(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn debug(&self, message: &str, context: &[(&str, &str)]) { - todo!() - } - - fn log(&self, level: &str, message: &str, context: &[(&str, &str)]) { - todo!() - } -} |
