diff options
24 files changed, 190 insertions, 159 deletions
@@ -1011,6 +1011,7 @@ name = "shirabe-php-shim" version = "0.0.1" dependencies = [ "anyhow", + "chrono", "indexmap", "serde", "zip", diff --git a/crates/shirabe-php-shim/Cargo.toml b/crates/shirabe-php-shim/Cargo.toml index 30e132f..b2a2077 100644 --- a/crates/shirabe-php-shim/Cargo.toml +++ b/crates/shirabe-php-shim/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [dependencies] anyhow.workspace = true +chrono.workspace = true indexmap.workspace = true serde.workspace = true zip.workspace = true diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs index 835e1c1..433b8db 100644 --- a/crates/shirabe-php-shim/src/lib.rs +++ b/crates/shirabe-php-shim/src/lib.rs @@ -1740,8 +1740,6 @@ pub fn abs(_value: i64) -> i64 { todo!() } -pub const DATE_ATOM: &str = "Y-m-d\\TH:i:sP"; - pub fn ucfirst(_s: &str) -> String { todo!() } @@ -2409,7 +2407,6 @@ pub const OPENSSL_VERSION_NUMBER: i64 = 0; pub const OPENSSL_VERSION_TEXT: &str = ""; pub const PHP_BINARY: &str = ""; pub const PHP_WINDOWS_VERSION_BUILD: i64 = 0; -pub const DATE_RFC3339: &str = "Y-m-d\\TH:i:sP"; pub const PREG_BACKTRACK_LIMIT_ERROR: i64 = 2; #[derive(Debug, Clone)] @@ -2459,3 +2456,24 @@ pub fn zlib_decode(_data: &str) -> Option<String> { pub const STREAM_NOTIFY_FAILURE: i64 = 9; pub const STREAM_NOTIFY_FILE_SIZE_IS: i64 = 5; pub const STREAM_NOTIFY_PROGRESS: i64 = 7; + +pub fn date_create<Tz: chrono::TimeZone>(s: &str) -> chrono::ParseResult<chrono::DateTime<Tz>> { + todo!() +} + +/// PHP: \DATE_RFC3339 ("Y-m-d\TH:i:sP"). +pub const DATE_RFC3339: &str = "%Y-%m-%dT%H:%M:%S%:z"; + +/// PHP: \DATE_ATOM (equivalent to \DATE_RFC3339). +pub const DATE_ATOM: &str = DATE_RFC3339; + +/// Convert PHP-compatible date time format to strftime-compatible format. +/// Only the patterns Composer actually passes are supported; anything else panics. +pub fn date_format_to_strftime(format: &str) -> &'static str { + match format { + "Y-m-d H:i:s" => "%Y-%m-%d %H:%M:%S", + "Y-m-d" => "%Y-%m-%d", + "Ymd" => "%Y%m%d", + other => panic!("Unsupported PHP date format: {other:?}"), + } +} diff --git a/crates/shirabe/src/advisory/auditor.rs b/crates/shirabe/src/advisory/auditor.rs index 179278a..2b71237 100644 --- a/crates/shirabe/src/advisory/auditor.rs +++ b/crates/shirabe/src/advisory/auditor.rs @@ -6,8 +6,8 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; use shirabe_external_packages::symfony::console::formatter::OutputFormatter; use shirabe_php_shim::{ - InvalidArgumentException, PhpMixed, array_all, array_any, array_key_exists, array_keys, - array_reduce, get_class, sprintf, str_starts_with, + DATE_ATOM, InvalidArgumentException, PhpMixed, array_all, array_any, array_key_exists, + array_keys, array_reduce, get_class, sprintf, str_starts_with, }; use crate::advisory::AnySecurityAdvisory; @@ -464,11 +464,7 @@ impl Auditor { sa.title.clone(), self.get_url(sa), sa.affected_versions().get_pretty_string(), - // TODO(phase-b): PHP uses `$advisory->reportedAt->format(DATE_ATOM)`, but - // shim DATE_ATOM ("Y-m-d\TH:i:sP") is a PHP format string incompatible with - // chrono. Using the chrono equivalent directly; revisit once a PHP-style date - // formatter exists (see also locker.rs DATE_RFC3339). - sa.reported_at.format("%Y-%m-%dT%H:%M:%S%:z").to_string(), + sa.reported_at.format(DATE_ATOM).to_string(), ]; if let Some(ignored) = advisory.as_ignored() { headers.push("Ignore reason".to_string()); @@ -523,14 +519,7 @@ impl Auditor { "Affected versions: {}", OutputFormatter::escape(&sa.affected_versions().get_pretty_string()) )); - error.push(format!( - "Reported at: {}", - // TODO(phase-b): PHP uses `$advisory->reportedAt->format(DATE_ATOM)`, but - // shim DATE_ATOM ("Y-m-d\TH:i:sP") is a PHP format string incompatible with - // chrono. Using the chrono equivalent directly; revisit once a PHP-style date - // formatter exists (see also locker.rs DATE_RFC3339). - sa.reported_at.format("%Y-%m-%dT%H:%M:%S%:z") - )); + error.push(format!("Reported at: {}", sa.reported_at.format(DATE_ATOM))); if let Some(ignored) = advisory.as_ignored() { error.push(format!( "Ignore reason: {}", diff --git a/crates/shirabe/src/advisory/partial_security_advisory.rs b/crates/shirabe/src/advisory/partial_security_advisory.rs index 2062570..f72ecaa 100644 --- a/crates/shirabe/src/advisory/partial_security_advisory.rs +++ b/crates/shirabe/src/advisory/partial_security_advisory.rs @@ -4,10 +4,10 @@ use crate::advisory::AnySecurityAdvisory; use crate::advisory::SecurityAdvisory; use crate::package::version::VersionParser; use anyhow::Result; -use chrono::{DateTime, TimeZone, Utc}; +use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; -use shirabe_php_shim::{PhpMixed, UnexpectedValueException}; +use shirabe_php_shim::{DATE_RFC3339, PhpMixed, UnexpectedValueException}; use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::SimpleConstraint; @@ -57,12 +57,9 @@ impl PartialSecurityAdvisory { && data.contains_key("reportedAt"); if has_full_data { - let reported_at: DateTime<Utc> = Utc - .datetime_from_str( - data["reportedAt"].as_string().unwrap_or(""), - "%Y-%m-%dT%H:%M:%S+00:00", - ) - .unwrap_or_default(); + let reported_at: DateTime<Utc> = + shirabe_php_shim::date_create(data["reportedAt"].as_string().unwrap_or("")) + .unwrap_or_default(); let sources: Vec<IndexMap<String, String>> = data["sources"] .as_list() .map(|list| { diff --git a/crates/shirabe/src/advisory/security_advisory.rs b/crates/shirabe/src/advisory/security_advisory.rs index 2c5a309..7c258ee 100644 --- a/crates/shirabe/src/advisory/security_advisory.rs +++ b/crates/shirabe/src/advisory/security_advisory.rs @@ -2,17 +2,17 @@ use chrono::{DateTime, Utc}; use indexmap::IndexMap; +use shirabe_php_shim::DATE_RFC3339; use shirabe_semver::constraint::AnyConstraint; use crate::advisory::IgnoredSecurityAdvisory; use crate::advisory::PartialSecurityAdvisory; -/// Matches PHP's `format(DATE_RFC3339)`, e.g. "2020-01-01T00:00:00+00:00". fn serialize_date_rfc3339<S: serde::Serializer>( dt: &DateTime<Utc>, serializer: S, ) -> Result<S::Ok, S::Error> { - serializer.serialize_str(&dt.format("%Y-%m-%dT%H:%M:%S%:z").to_string()) + serializer.serialize_str(&dt.format(DATE_RFC3339).to_string()) } #[derive(Debug, Clone, serde::Serialize)] diff --git a/crates/shirabe/src/cache.rs b/crates/shirabe/src/cache.rs index 1a1f67f..74e3c15 100644 --- a/crates/shirabe/src/cache.rs +++ b/crates/shirabe/src/cache.rs @@ -7,8 +7,9 @@ use chrono::Utc; use shirabe_external_packages::composer::pcre::Preg; use shirabe_external_packages::symfony::finder::Finder; use shirabe_php_shim::{ - abs, bin2hex, dirname, file_exists, file_get_contents, file_put_contents, filemtime, hash_file, - is_dir, is_writable, mkdir, random_bytes, random_int, rename, time, unlink, + abs, bin2hex, date_format_to_strftime, dirname, file_exists, file_get_contents, + file_put_contents, filemtime, hash_file, is_dir, is_writable, mkdir, random_bytes, random_int, + rename, time, unlink, }; use crate::io::IOInterface; @@ -278,12 +279,13 @@ impl Cache { pub fn gc(&mut self, ttl: i64, max_size: i64) -> bool { if self.is_enabled() && !self.read_only { - let mut expire = Utc::now(); - // PHP: $expire->modify('-'.$ttl.' seconds'); - expire -= chrono::Duration::seconds(ttl); + let expire = Utc::now() - chrono::Duration::seconds(ttl); let mut finder = self.get_finder(); - finder.date(&format!("until {}", expire.format("%Y-%m-%d %H:%M:%S"))); + finder.date(&format!( + "until {}", + expire.format(date_format_to_strftime("Y-m-d H:i:s")) + )); for file in &mut finder { let _ = self.filesystem.borrow_mut().unlink(&file.get_pathname()); } @@ -309,15 +311,17 @@ impl Cache { pub fn gc_vcs_cache(&mut self, ttl: i64) -> bool { if self.is_enabled() { - let mut expire = Utc::now(); - expire -= chrono::Duration::seconds(ttl); + let expire = Utc::now() - chrono::Duration::seconds(ttl); let mut finder = Finder::create(); finder .r#in(&self.root) .directories() .depth(0) - .date(&format!("until {}", expire.format("%Y-%m-%d %H:%M:%S"))); + .date(&format!( + "until {}", + expire.format(date_format_to_strftime("Y-m-d H:i:s")) + )); for file in &mut finder { let _ = self .filesystem diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs index e99005e..7ee103a 100644 --- a/crates/shirabe/src/command/show_command.rs +++ b/crates/shirabe/src/command/show_command.rs @@ -9,8 +9,9 @@ use shirabe_external_packages::symfony::console::formatter::OutputFormatterStyle use shirabe_external_packages::symfony::console::input::InputInterface; use shirabe_external_packages::symfony::console::output::OutputInterface; use shirabe_php_shim::{ - InvalidArgumentException, LogicException, PhpMixed, UnexpectedValueException, array_search, - date, extension_loaded, in_array, realpath, strtolower, version_compare, + DATE_ATOM, InvalidArgumentException, LogicException, PhpMixed, UnexpectedValueException, + array_search, date, date_format_to_strftime, extension_loaded, in_array, realpath, strtolower, + version_compare, }; use shirabe_semver::constraint::AnyConstraint; @@ -1026,7 +1027,7 @@ impl ShowCommand { .insert("release-age".to_string(), PhpMixed::String(age)); package_view_data.insert( "release-date".to_string(), - PhpMixed::String(release_date.to_rfc3339()), + PhpMixed::String(release_date.format(DATE_ATOM).to_string()), ); } else { package_view_data.insert( @@ -1062,7 +1063,7 @@ impl ShowCommand { if let Some(rd) = latest.get_release_date() { package_view_data.insert( "latest-release-date".to_string(), - PhpMixed::String(rd.to_rfc3339()), + PhpMixed::String(rd.format(DATE_ATOM).to_string()), ); } else { package_view_data.insert( @@ -1666,7 +1667,7 @@ impl ShowCommand { let rel = self.get_relative_time(&rd); self.get_io().write(&format!( "<info>released</info> : {}, {}", - rd.format("%Y-%m-%d"), + rd.format(date_format_to_strftime("Y-m-d")), rel )); } @@ -1677,7 +1678,11 @@ impl ShowCommand { None => String::new(), Some(rd) => { let rel = self.get_relative_time(&rd); - format!(" released {}, {}", rd.format("%Y-%m-%d"), rel) + format!( + " released {}, {}", + rd.format(date_format_to_strftime("Y-m-d")), + rel + ) } }; self.get_io().write(&format!( @@ -2017,7 +2022,10 @@ impl ShowCommand { } if let Some(rd) = package.get_release_date() { - json.insert("released".to_string(), PhpMixed::String(rd.to_rfc3339())); + json.insert( + "released".to_string(), + PhpMixed::String(rd.format(DATE_ATOM).to_string()), + ); } } @@ -2786,12 +2794,15 @@ impl ShowCommand { } fn get_relative_time(&self, release_date: &chrono::DateTime<chrono::Utc>) -> String { - if release_date.format("%Y%m%d").to_string() == date("Ymd", None) { + if release_date + .format(date_format_to_strftime("Ymd")) + .to_string() + == date("Ymd", None) + { return "today".to_string(); } - let now: chrono::DateTime<chrono::Utc> = chrono::Utc::now(); - let diff = now.signed_duration_since(*release_date); + let diff = chrono::Utc::now().signed_duration_since(*release_date); let days = diff.num_days(); if days < 7 { return "this week".to_string(); diff --git a/crates/shirabe/src/compiler.rs b/crates/shirabe/src/compiler.rs index 1088477..05177d6 100644 --- a/crates/shirabe/src/compiler.rs +++ b/crates/shirabe/src/compiler.rs @@ -9,8 +9,8 @@ use shirabe_external_packages::symfony::finder::Finder; use shirabe_external_packages::symfony::finder::SplFileInfo; use shirabe_php_shim::{ Phar, PhpMixed, RuntimeException, T_COMMENT, T_DOC_COMMENT, T_WHITESPACE, - UnexpectedValueException, array_search, file_exists, file_get_contents, strcmp, strtr, - strtr_array, token_get_all, + UnexpectedValueException, array_search, date_format_to_strftime, file_exists, + file_get_contents, strcmp, strtr, strtr_array, token_get_all, }; use crate::json::JsonFile; @@ -95,8 +95,7 @@ impl Compiler { let version_date_str = Git::parse_rev_list_output(&output, &process); self.version_date = chrono::DateTime::parse_from_str(version_date_str.trim(), "%Y-%m-%d %H:%M:%S %z") - .map(|dt| dt.with_timezone(&chrono::Utc)) - .unwrap_or_else(|_| chrono::Utc::now()); + .map(|dt| dt.with_timezone(&chrono::Utc))?; let mut git_describe_output = String::new(); if process.borrow_mut().execute_args( @@ -298,7 +297,12 @@ impl Compiler { // re-sign the phar with reproducible timestamp / signature let mut util = Timestamps::new(phar_file); - util.update_timestamps(&self.version_date.format("%Y-%m-%d %H:%M:%S").to_string())?; + util.update_timestamps( + &self + .version_date + .format(date_format_to_strftime("Y-m-d H:i:s")) + .to_string(), + )?; util.save(phar_file, Phar::SHA512)?; Linter::lint( @@ -355,7 +359,9 @@ impl Compiler { ); replacements.insert( "@release_date@".to_string(), - self.version_date.format("%Y-%m-%d %H:%M:%S").to_string(), + self.version_date + .format(date_format_to_strftime("Y-m-d H:i:s")) + .to_string(), ); content = strtr_array(&content, &replacements); content = Preg::replace( diff --git a/crates/shirabe/src/package/dumper/array_dumper.rs b/crates/shirabe/src/package/dumper/array_dumper.rs index 4217308..27fc415 100644 --- a/crates/shirabe/src/package/dumper/array_dumper.rs +++ b/crates/shirabe/src/package/dumper/array_dumper.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Package/Dumper/ArrayDumper.php use indexmap::IndexMap; -use shirabe_php_shim::PhpMixed; +use shirabe_php_shim::{DATE_RFC3339, PhpMixed}; use crate::package::Mirror; use crate::package::PackageInterfaceHandle; @@ -156,7 +156,7 @@ impl ArrayDumper { if let Some(release_date) = package.get_release_date() { data.insert( "time".to_string(), - PhpMixed::String(release_date.to_rfc3339()), + PhpMixed::String(release_date.format(DATE_RFC3339).to_string()), ); } diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs index d2aeac6..4df022b 100644 --- a/crates/shirabe/src/package/loader/array_loader.rs +++ b/crates/shirabe/src/package/loader/array_loader.rs @@ -1,12 +1,12 @@ //! ref: composer/src/Composer/Package/Loader/ArrayLoader.php use anyhow::Result; -use chrono::{DateTime, TimeZone, Utc}; +use chrono::Utc; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; use shirabe_php_shim::{ - E_USER_DEPRECATED, Exception, PhpMixed, UnexpectedValueException, is_scalar, is_string, - json_encode, ltrim, sprintf, stripos, strpos, strtolower, strval, substr, trigger_error, trim, + E_USER_DEPRECATED, PhpMixed, UnexpectedValueException, is_scalar, is_string, json_encode, + ltrim, sprintf, stripos, strpos, strtolower, strval, substr, trigger_error, trim, }; use crate::package::CompleteAliasPackageHandle; @@ -527,14 +527,7 @@ impl ArrayLoader { time_str.to_string() }; - let result: std::result::Result<DateTime<Utc>, Exception> = - // TODO(phase-b): port PHP `new \DateTime($time, new \DateTimeZone('UTC'))` - Utc.datetime_from_str(&time, "%Y-%m-%dT%H:%M:%S%z") - .map_err(|e| Exception { - message: e.to_string(), - code: 0, - }); - if let Ok(date) = result { + if let Ok(date) = shirabe_php_shim::date_create::<Utc>(&time) { package.package_mut().set_release_date(Some(date)); } } diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs index 4994d5a..e6db19e 100644 --- a/crates/shirabe/src/package/loader/validating_array_loader.rs +++ b/crates/shirabe/src/package/loader/validating_array_loader.rs @@ -6,10 +6,10 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; use shirabe_external_packages::composer::spdx_licenses::SpdxLicenses; use shirabe_php_shim::{ - E_USER_DEPRECATED, Exception, FILTER_VALIDATE_EMAIL, PHP_EOL, PhpMixed, array_intersect_key, - array_values, filter_var, get_debug_type, is_array, is_bool, is_int, is_numeric, is_scalar, - is_string, json_encode, parse_url_all, php_to_string, sprintf, str_replace, strcasecmp, - strtolower, strtotime, substr, trigger_error, trim, var_export, + E_USER_DEPRECATED, FILTER_VALIDATE_EMAIL, PHP_EOL, PhpMixed, array_intersect_key, array_values, + filter_var, get_debug_type, is_array, is_bool, is_int, is_numeric, is_scalar, is_string, + json_encode, parse_url_all, php_to_string, sprintf, str_replace, strcasecmp, strtolower, + strtotime, substr, trigger_error, trim, var_export, }; use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchNoneConstraint; @@ -166,7 +166,7 @@ impl ValidatingArrayLoader { self.validate_string("time", false); if self.config.contains_key("time") { let time_str = self.config["time"].as_string().unwrap_or("").to_string(); - match Self::parse_datetime_utc(&time_str) { + match shirabe_php_shim::date_create::<chrono::Utc>(&time_str) { Ok(dt) => { release_date = Some(dt); } @@ -1591,22 +1591,4 @@ impl ValidatingArrayLoader { None => true, } } - - fn parse_datetime_utc(s: &str) -> anyhow::Result<chrono::DateTime<chrono::Utc>> { - // TODO(phase-b): PHP's `new \DateTime($s, new \DateTimeZone('UTC'))` accepts - // many free-form formats; approximate with chrono for now. - if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(s) { - return Ok(dt.with_timezone(&chrono::Utc)); - } - if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") { - return Ok(chrono::Utc.from_utc_datetime(&dt)); - } - if let Ok(d) = chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d") { - return Ok(chrono::Utc.from_utc_datetime(&d.and_hms_opt(0, 0, 0).unwrap())); - } - Err(anyhow::anyhow!(Exception { - message: format!("Failed to parse date: {}", s), - code: 0, - })) - } } diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index 5f13a31..2546780 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -876,7 +876,6 @@ impl Locker { None, ); if Preg::is_match(r"{^\s*\d+\s*$}", &output_str).unwrap_or(false) { - // TODO(phase-b): new \DateTime('@'.trim($output), new \DateTimeZone('UTC')) let ts = trim(&output_str, None).parse::<i64>().unwrap_or(0); datetime = chrono::DateTime::from_timestamp(ts, 0); } diff --git a/crates/shirabe/src/repository/vcs/forgejo_driver.rs b/crates/shirabe/src/repository/vcs/forgejo_driver.rs index a40f6a9..73975cd 100644 --- a/crates/shirabe/src/repository/vcs/forgejo_driver.rs +++ b/crates/shirabe/src/repository/vcs/forgejo_driver.rs @@ -5,7 +5,7 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_php_shim::{ - PhpMixed, RuntimeException, base64_decode, explode, extension_loaded, urlencode, + DATE_RFC3339, PhpMixed, RuntimeException, base64_decode, explode, extension_loaded, urlencode, }; use crate::cache::Cache; @@ -185,7 +185,7 @@ impl ForgejoDriver { pub fn get_change_date( &mut self, identifier: &str, - ) -> Result<Option<chrono::DateTime<chrono::Utc>>> { + ) -> Result<Option<chrono::DateTime<chrono::FixedOffset>>> { if let Some(ref mut git_driver) = self.git_driver { return git_driver.get_change_date(identifier); } @@ -218,8 +218,7 @@ impl ForgejoDriver { code: 0, })?; - let date = chrono::DateTime::parse_from_rfc3339(&date_str) - .map(|d| d.with_timezone(&chrono::Utc))?; + let date: chrono::DateTime<chrono::FixedOffset> = shirabe_php_shim::date_create(&date_str)?; Ok(Some(date)) } @@ -725,7 +724,7 @@ impl crate::repository::vcs::VcsDriverInterface for ForgejoDriver { fn get_change_date( &mut self, identifier: &str, - ) -> anyhow::Result<Option<chrono::DateTime<chrono::Utc>>> { + ) -> anyhow::Result<Option<chrono::DateTime<chrono::FixedOffset>>> { ForgejoDriver::get_change_date(self, identifier) } diff --git a/crates/shirabe/src/repository/vcs/fossil_driver.rs b/crates/shirabe/src/repository/vcs/fossil_driver.rs index 0ee20da..15328c0 100644 --- a/crates/shirabe/src/repository/vcs/fossil_driver.rs +++ b/crates/shirabe/src/repository/vcs/fossil_driver.rs @@ -1,10 +1,12 @@ //! ref: composer/src/Composer/Repository/Vcs/FossilDriver.php use crate::io::io_interface; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, FixedOffset, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; -use shirabe_php_shim::{PhpMixed, RuntimeException, dirname, is_dir, is_file, is_writable}; +use shirabe_php_shim::{ + DATE_RFC3339, PhpMixed, RuntimeException, dirname, is_dir, is_file, is_writable, +}; use crate::cache::Cache; use crate::config::Config; @@ -256,7 +258,10 @@ impl FossilDriver { Ok(Some(content)) } - pub fn get_change_date(&self, _identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + pub fn get_change_date( + &self, + _identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { let mut output = String::new(); self.inner.process.borrow_mut().execute_args( &["fossil", "finfo", "-b", "-n", "1", "composer.json"] @@ -268,8 +273,8 @@ impl FossilDriver { let parts: Vec<&str> = output.trim().splitn(3, ' ').collect(); let date = parts.get(1).copied().unwrap_or(""); - let date = DateTime::parse_from_rfc3339(date).map(|d| d.with_timezone(&Utc))?; - Ok(Some(date)) + let date: DateTime<Utc> = shirabe_php_shim::date_create(date)?; + Ok(Some(date.fixed_offset())) } pub fn get_tags(&mut self) -> anyhow::Result<IndexMap<String, String>> { @@ -383,7 +388,10 @@ impl crate::repository::vcs::VcsDriverInterface for FossilDriver { FossilDriver::get_file_content(self, file, identifier) } - fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + fn get_change_date( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { FossilDriver::get_change_date(self, identifier) } diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs index f8cfd7a..de1f814 100644 --- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs +++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs @@ -2,13 +2,13 @@ use crate::io::io_interface; use anyhow::Result; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, FixedOffset}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_php_shim::{ - InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_key_exists, - array_search_mixed, extension_loaded, http_build_query_mixed, implode, in_array, is_array, - sprintf, strpos, + DATE_RFC3339, InvalidArgumentException, LogicException, PhpMixed, RuntimeException, + array_key_exists, array_search_mixed, extension_loaded, http_build_query_mixed, implode, + in_array, is_array, sprintf, strpos, }; use crate::cache::Cache; @@ -461,7 +461,7 @@ impl GitBitbucketDriver { } /// @inheritDoc - pub fn get_change_date(&mut self, identifier: &str) -> Result<Option<DateTime<Utc>>> { + pub fn get_change_date(&mut self, identifier: &str) -> Result<Option<DateTime<FixedOffset>>> { if let Some(fallback) = self.fallback_driver.as_mut() { return fallback.get_change_date(identifier); } @@ -486,11 +486,8 @@ impl GitBitbucketDriver { .fetch_with_oauth_credentials(&resource, false)? .decode_json()?; - // TODO(phase-b): port PHP `new \DateTimeImmutable($commit['date'])` let date_str = commit.get("date").and_then(|v| v.as_string()).unwrap_or(""); - let date: DateTime<Utc> = chrono::DateTime::parse_from_rfc3339(date_str) - .map_err(|e| anyhow::anyhow!(e))? - .with_timezone(&Utc); + let date: DateTime<FixedOffset> = shirabe_php_shim::date_create(date_str)?; Ok(Some(date)) } @@ -920,7 +917,10 @@ impl crate::repository::vcs::VcsDriverInterface for GitBitbucketDriver { GitBitbucketDriver::get_file_content(self, file, identifier) } - fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + fn get_change_date( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { GitBitbucketDriver::get_change_date(self, identifier) } diff --git a/crates/shirabe/src/repository/vcs/git_driver.rs b/crates/shirabe/src/repository/vcs/git_driver.rs index 4878ffe..7f8c206 100644 --- a/crates/shirabe/src/repository/vcs/git_driver.rs +++ b/crates/shirabe/src/repository/vcs/git_driver.rs @@ -2,7 +2,7 @@ use crate::io::io_interface; use chrono::TimeZone; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, FixedOffset, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_php_shim::{ @@ -281,7 +281,10 @@ impl GitDriver { Ok(Some(content)) } - pub fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + pub fn get_change_date( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { if identifier.starts_with('-') { return Err(RuntimeException { message: format!( @@ -310,7 +313,9 @@ impl GitDriver { let timestamp_str = GitUtil::parse_rev_list_output(&output, &self.inner.process); let timestamp: i64 = timestamp_str.trim().parse().unwrap_or(0); - Ok(Some(Utc.timestamp_opt(timestamp, 0).unwrap())) + Ok(Some( + Utc.timestamp_opt(timestamp, 0).unwrap().fixed_offset(), + )) } pub fn get_tags(&mut self) -> anyhow::Result<IndexMap<String, String>> { @@ -514,7 +519,10 @@ impl crate::repository::vcs::VcsDriverInterface for GitDriver { GitDriver::get_file_content(self, file, identifier) } - fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + fn get_change_date( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { GitDriver::get_change_date(self, identifier) } diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs index 45ba7b7..aa1e135 100644 --- a/crates/shirabe/src/repository/vcs/github_driver.rs +++ b/crates/shirabe/src/repository/vcs/github_driver.rs @@ -2,13 +2,14 @@ use crate::io::io_interface; use anyhow::Result; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, FixedOffset}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_php_shim::{ - InvalidArgumentException, PhpMixed, RuntimeException, array_diff, array_key_exists, array_map, - array_search_mixed, base64_decode, basename, count, empty, explode, extension_loaded, in_array, - parse_url_all, sprintf, strpos, strtolower, substr, trim, urlencode, + DATE_RFC3339, InvalidArgumentException, PhpMixed, RuntimeException, array_diff, + array_key_exists, array_map, array_search_mixed, base64_decode, basename, count, empty, + explode, extension_loaded, in_array, parse_url_all, sprintf, strpos, strtolower, substr, trim, + urlencode, }; use crate::cache::Cache; @@ -821,7 +822,7 @@ impl GitHubDriver { Ok(Some(content)) } - pub fn get_change_date(&mut self, identifier: &str) -> Result<Option<DateTime<Utc>>> { + pub fn get_change_date(&mut self, identifier: &str) -> Result<Option<DateTime<FixedOffset>>> { if let Some(ref mut git_driver) = self.git_driver { return git_driver.get_change_date(identifier); } @@ -851,11 +852,8 @@ impl GitHubDriver { _ => String::new(), }; - Ok(Some( - DateTime::parse_from_rfc3339(&date_str) - .map(|dt| dt.with_timezone(&Utc)) - .unwrap_or_else(|_| Utc::now()), - )) + let date: DateTime<FixedOffset> = shirabe_php_shim::date_create(&date_str)?; + Ok(Some(date)) } pub fn get_tags(&mut self) -> Result<IndexMap<String, String>> { @@ -1364,7 +1362,10 @@ impl crate::repository::vcs::VcsDriverInterface for GitHubDriver { GitHubDriver::get_file_content(self, file, identifier) } - fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + fn get_change_date( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { GitHubDriver::get_change_date(self, identifier) } diff --git a/crates/shirabe/src/repository/vcs/gitlab_driver.rs b/crates/shirabe/src/repository/vcs/gitlab_driver.rs index 97dcc30..8ab30d4 100644 --- a/crates/shirabe/src/repository/vcs/gitlab_driver.rs +++ b/crates/shirabe/src/repository/vcs/gitlab_driver.rs @@ -2,13 +2,13 @@ use crate::io::io_interface; use anyhow::Result; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, FixedOffset}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_php_shim::{ - InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_search_mixed, - array_shift, ctype_alnum, empty, explode, extension_loaded, implode, in_array, is_array, - is_string, ord, sprintf, strpos, strtolower, + DATE_RFC3339, InvalidArgumentException, LogicException, PhpMixed, RuntimeException, + array_search_mixed, array_shift, ctype_alnum, empty, explode, extension_loaded, implode, + in_array, is_array, is_string, ord, sprintf, strpos, strtolower, }; use crate::cache::Cache; @@ -458,7 +458,7 @@ impl GitLabDriver { Ok(content) } - pub fn get_change_date(&mut self, identifier: &str) -> Result<Option<DateTime<Utc>>> { + pub fn get_change_date(&mut self, identifier: &str) -> Result<Option<DateTime<FixedOffset>>> { if let Some(ref mut git_driver) = self.git_driver { return git_driver.get_change_date(identifier); } @@ -468,11 +468,8 @@ impl GitLabDriver { .get("committed_date") .and_then(|v| v.as_string()) .unwrap_or(""); - return Ok(Some( - DateTime::parse_from_rfc3339(committed_date) - .map(|dt| dt.with_timezone(&Utc)) - .unwrap_or_else(|_| Utc::now()), - )); + let date: DateTime<FixedOffset> = shirabe_php_shim::date_create(committed_date)?; + return Ok(Some(date)); } Ok(None) @@ -1153,7 +1150,10 @@ impl crate::repository::vcs::VcsDriverInterface for GitLabDriver { GitLabDriver::get_file_content(self, file, identifier) } - fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + fn get_change_date( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { GitLabDriver::get_change_date(self, identifier) } diff --git a/crates/shirabe/src/repository/vcs/hg_driver.rs b/crates/shirabe/src/repository/vcs/hg_driver.rs index c35a574..f637437 100644 --- a/crates/shirabe/src/repository/vcs/hg_driver.rs +++ b/crates/shirabe/src/repository/vcs/hg_driver.rs @@ -10,10 +10,10 @@ use crate::repository::vcs::VcsDriverBase; use crate::util::Filesystem; use crate::util::Hg as HgUtils; use crate::util::Url; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, FixedOffset, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; -use shirabe_php_shim::{PhpMixed, RuntimeException, dirname, is_dir, is_writable}; +use shirabe_php_shim::{DATE_RFC3339, PhpMixed, RuntimeException, dirname, is_dir, is_writable}; #[derive(Debug)] pub struct HgDriver { @@ -197,7 +197,10 @@ impl HgDriver { Ok(Some(content)) } - pub fn get_change_date(&self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + pub fn get_change_date( + &self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { if identifier.starts_with('-') { return Err(RuntimeException { message: format!( @@ -225,8 +228,8 @@ impl HgDriver { Some(self.repo_dir.clone()), ); - let date = DateTime::parse_from_rfc3339(output.trim()).map(|d| d.with_timezone(&Utc))?; - Ok(Some(date)) + let date: DateTime<Utc> = shirabe_php_shim::date_create(output.trim())?; + Ok(Some(date.fixed_offset())) } pub fn get_tags(&mut self) -> anyhow::Result<IndexMap<String, String>> { @@ -410,7 +413,10 @@ impl crate::repository::vcs::VcsDriverInterface for HgDriver { HgDriver::get_file_content(self, file, identifier) } - fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + fn get_change_date( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { HgDriver::get_change_date(self, identifier) } diff --git a/crates/shirabe/src/repository/vcs/perforce_driver.rs b/crates/shirabe/src/repository/vcs/perforce_driver.rs index 0fe0f6f..65f2856 100644 --- a/crates/shirabe/src/repository/vcs/perforce_driver.rs +++ b/crates/shirabe/src/repository/vcs/perforce_driver.rs @@ -113,7 +113,7 @@ impl PerforceDriver { pub fn get_change_date( &self, _identifier: &str, - ) -> anyhow::Result<Option<chrono::DateTime<chrono::Utc>>> { + ) -> anyhow::Result<Option<chrono::DateTime<chrono::FixedOffset>>> { Ok(None) } @@ -248,7 +248,7 @@ impl crate::repository::vcs::VcsDriverInterface for PerforceDriver { fn get_change_date( &mut self, identifier: &str, - ) -> anyhow::Result<Option<chrono::DateTime<chrono::Utc>>> { + ) -> anyhow::Result<Option<chrono::DateTime<chrono::FixedOffset>>> { PerforceDriver::get_change_date(self, identifier) } diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs index ef8787a..f4e2bba 100644 --- a/crates/shirabe/src/repository/vcs/svn_driver.rs +++ b/crates/shirabe/src/repository/vcs/svn_driver.rs @@ -1,7 +1,7 @@ //! ref: composer/src/Composer/Repository/Vcs/SvnDriver.php use anyhow::Result; -use chrono::{DateTime, TimeZone, Utc}; +use chrono::{DateTime, FixedOffset, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_php_shim::{ @@ -296,7 +296,7 @@ impl SvnDriver { Ok(Some(output)) } - pub fn get_change_date(&mut self, identifier: &str) -> Result<Option<DateTime<Utc>>> { + pub fn get_change_date(&mut self, identifier: &str) -> Result<Option<DateTime<FixedOffset>>> { let identifier = format!("/{}/", trim(identifier, Some("/"))); let (path, rev) = if let Ok(Some(m)) = @@ -329,10 +329,9 @@ impl SvnDriver { .unwrap_or(false) { let date_str = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(); - // PHP: new \DateTimeImmutable($match[1], new \DateTimeZone('UTC')) - return Ok(Utc - .datetime_from_str(date_str.trim(), "%Y-%m-%d %H:%M:%S %z") - .ok()); + return Ok(shirabe_php_shim::date_create::<Utc>(date_str.trim()) + .ok() + .map(|d| d.fixed_offset())); } } } @@ -638,7 +637,10 @@ impl crate::repository::vcs::VcsDriverInterface for SvnDriver { SvnDriver::get_file_content(self, file, identifier) } - fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> { + fn get_change_date( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>> { SvnDriver::get_change_date(self, identifier) } diff --git a/crates/shirabe/src/repository/vcs/vcs_driver.rs b/crates/shirabe/src/repository/vcs/vcs_driver.rs index 94c3ff8..dc88387 100644 --- a/crates/shirabe/src/repository/vcs/vcs_driver.rs +++ b/crates/shirabe/src/repository/vcs/vcs_driver.rs @@ -1,9 +1,9 @@ //! ref: composer/src/Composer/Repository/Vcs/VcsDriver.php -use chrono::{DateTime, Utc}; +use chrono::{DateTime, FixedOffset}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; -use shirabe_php_shim::{PhpMixed, extension_loaded}; +use shirabe_php_shim::{DATE_RFC3339, PhpMixed, extension_loaded}; use crate::cache::Cache; use crate::config::Config; @@ -94,7 +94,7 @@ impl VcsDriverBase { pub fn finish_base_composer_information( identifier: &str, composer_file_content: Option<String>, - change_date: impl FnOnce() -> anyhow::Result<Option<DateTime<Utc>>>, + change_date: impl FnOnce() -> anyhow::Result<Option<DateTime<FixedOffset>>>, ) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> { let content = match composer_file_content { None => return Ok(None), @@ -123,7 +123,10 @@ impl VcsDriverBase { .map_or(true, |v| v.as_string().map_or(true, |s| s.is_empty())) { if let Some(d) = change_date()? { - composer.insert("time".to_string(), PhpMixed::String(d.to_rfc3339())); + composer.insert( + "time".to_string(), + PhpMixed::String(d.format(DATE_RFC3339).to_string()), + ); } } @@ -284,7 +287,7 @@ pub trait VcsDriver: VcsDriverInterface { if let Some(change_date) = self.get_change_date(identifier)? { composer.insert( "time".to_string(), - PhpMixed::String(change_date.to_rfc3339()), + PhpMixed::String(change_date.format(DATE_RFC3339).to_string()), ); } } diff --git a/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs b/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs index e845300..5d2be04 100644 --- a/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs +++ b/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs @@ -2,7 +2,7 @@ use crate::config::Config; use crate::io::IOInterface; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, FixedOffset}; use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; use std::cell::RefCell; @@ -18,7 +18,10 @@ pub trait VcsDriverInterface: std::fmt::Debug { fn get_file_content(&mut self, file: &str, identifier: &str) -> anyhow::Result<Option<String>>; - fn get_change_date(&mut self, identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>>; + fn get_change_date( + &mut self, + identifier: &str, + ) -> anyhow::Result<Option<DateTime<FixedOffset>>>; fn get_root_identifier(&mut self) -> anyhow::Result<String>; |
