From 3334eff9931f895ddf6c6bcefe4857c66109e05e Mon Sep 17 00:00:00 2001 From: Barsik Date: Fri, 8 Mar 2024 13:03:32 +0200 Subject: [PATCH 01/20] Add tests for handling spaces in command arguments Implemented new tests checking the parser's ability to handle command arguments that include spaces. Tests cover both command values and properties. Also, added a command aggregator test for subjects containing spaces. This prepares the code for a future feature that requires parsing such spaces. --- .../tests/inc/commands_aggregator/basic.rs | 23 +++++++++ .../wca/tests/inc/commands_aggregator/mod.rs | 1 + module/move/wca/tests/inc/parser/command.rs | 51 +++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/module/move/wca/tests/inc/commands_aggregator/basic.rs b/module/move/wca/tests/inc/commands_aggregator/basic.rs index 8ff33fe48b..cca8288d3a 100644 --- a/module/move/wca/tests/inc/commands_aggregator/basic.rs +++ b/module/move/wca/tests/inc/commands_aggregator/basic.rs @@ -380,6 +380,28 @@ tests_impls! a_id!( grammar_command.subjects, vec![ TheModule::Value::String("qwe:rty".into()) ] ); } + + fn subject_with_spaces() + { + let query = "SELECT title, links, MIN( published ) FROM Frames"; + let ca = CommandsAggregator::former() + .grammar( + [ + wca::Command::former() + .hint( "hint" ) + .long_hint( "long_hint" ) + .phrase( "query.execute" ) + .subject( "SQL query", Type::String, false ) + .form(), + ]) + .executor( + [ + ( "query.execute".to_owned(), Routine::new( move |( args, _ )| { assert_eq!( query, args.get_owned::< &str >( 0 ).unwrap() ); Ok( () ) } ) ), + ]) + .build(); + + a_id!( (), ca.perform( vec![ ".query.execute".to_string(), query.into() ] ).unwrap() ); + } } // @@ -396,4 +418,5 @@ tests_index! string_subject_with_colon, no_prop_subject_with_colon, optional_prop_subject_with_colon, + subject_with_spaces, } diff --git a/module/move/wca/tests/inc/commands_aggregator/mod.rs b/module/move/wca/tests/inc/commands_aggregator/mod.rs index 1d200e22d1..3aad7e49e6 100644 --- a/module/move/wca/tests/inc/commands_aggregator/mod.rs +++ b/module/move/wca/tests/inc/commands_aggregator/mod.rs @@ -7,6 +7,7 @@ use wca:: CommandsAggregator, Routine, + Type, HelpVariants, Error, ValidationError, diff --git a/module/move/wca/tests/inc/parser/command.rs b/module/move/wca/tests/inc/parser/command.rs index ec51a8afd9..4656fa7d95 100644 --- a/module/move/wca/tests/inc/parser/command.rs +++ b/module/move/wca/tests/inc/parser/command.rs @@ -146,6 +146,56 @@ tests_impls! ); } + // qqq : the parser must be able to accept a list of arguments(std::env::args()) + fn with_spaces_in_value() + { + let parser = Parser::former().form(); + + a_id! + ( + ParsedCommand + { + name : "command".into(), + subjects : vec![ "value with spaces".into() ], + properties : HashMap::new(), + }, + parser.command( vec![ ".command".to_string(), "value with spaces".into() ] ).unwrap() + ); + + a_id! + ( + ParsedCommand + { + name : "command".into(), + subjects : vec![], + properties : HashMap::from_iter([ ( "prop".into(), "value with spaces".into() ) ]), + }, + parser.command( vec![ ".command".to_string(), "prop:value with spaces".into() ] ).unwrap() + ); + + a_id! + ( + ParsedCommand + { + name : "command".into(), + subjects : vec![], + properties : HashMap::from_iter([ ( "prop".into(), "value with spaces".into() ) ]), + }, + parser.command( vec![ ".command".to_string(), "prop:".into(), "value with spaces".into() ] ).unwrap() + ); + + a_id! + ( + ParsedCommand + { + name : "command".into(), + subjects : vec![], + properties : HashMap::from_iter([ ( "prop".into(), "value with spaces".into() ) ]), + }, + parser.command( vec![ ".command".to_string(), "prop".into(), ":".into(), "value with spaces".into() ] ).unwrap() + ); + } + fn not_only_alphanumeric_symbols() { let parser = Parser::former().form(); @@ -387,6 +437,7 @@ tests_index! { basic, with_spaces, + with_spaces_in_value, not_only_alphanumeric_symbols, same_command_and_prop_delimeter, path_in_subject, From 2308c56fa05867d6e9c318fad33cc3fec271e1e3 Mon Sep 17 00:00:00 2001 From: Barsik-sus Date: Thu, 14 Mar 2024 15:10:38 +0200 Subject: [PATCH 02/20] wip: Describe publish plan and related to the process sub plans --- module/move/willbe/src/entity/package.rs | 213 +++++++++++++++++++++-- module/move/willbe/src/entity/version.rs | 2 +- module/move/willbe/src/tool/cargo.rs | 26 +-- module/move/willbe/src/tool/path.rs | 24 ++- 4 files changed, 237 insertions(+), 28 deletions(-) diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index ec4e476d18..0bb6a682e1 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -4,8 +4,7 @@ mod private use std:: { - path::Path, - collections::{ HashMap, HashSet }, + collections::{ HashMap, HashSet }, path::Path, }; use std::fmt::Formatter; use std::hash::Hash; @@ -184,16 +183,16 @@ mod private match self { Self::Manifest( manifest ) => - { - let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; + { + let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; - // Unwrap safely because of the `Package` type guarantee - Ok( data[ "package" ].get( "metadata" ).and_then( | m | m.get( "stability" ) ).and_then( | s | s.as_str() ).and_then( | s | s.parse::< Stability >().ok() ).unwrap_or( Stability::Experimental) ) - } + // Unwrap safely because of the `Package` type guarantee + Ok( data[ "package" ].get( "metadata" ).and_then( | m | m.get( "stability" ) ).and_then( | s | s.as_str() ).and_then( | s | s.parse::< Stability >().ok() ).unwrap_or( Stability::Experimental) ) + } Self::Metadata( metadata ) => - { - Ok( metadata.metadata["stability"].as_str().and_then( | s | s.parse::< Stability >().ok() ).unwrap_or( Stability::Experimental) ) - } + { + Ok( metadata.metadata["stability"].as_str().and_then( | s | s.parse::< Stability >().ok() ).unwrap_or( Stability::Experimental) ) + } } } @@ -279,6 +278,194 @@ mod private } } + pub trait Plan + { + type Report; + fn perform( &self, dry : bool ) -> Result< Self::Report >; + } + + pub struct CargoPackagePlan + { + crate_dir : CrateDir, + } + + impl Plan for CargoPackagePlan + { + type Report = process::CmdReport; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let args = cargo::PackOptions::former() + .path( self.crate_dir.as_ref() ) + .dry( dry ) + .form(); + + Ok( cargo::pack( args )? ) + } + } + + pub struct VersionBumpPlan + { + pub crate_dir : CrateDir, + pub old_version : version::Version, + pub new_version : version::Version, + pub dependencies : Vec< CrateDir >, + } + + impl Plan for VersionBumpPlan + { + type Report = ExtendedBumpReport; + fn perform( &self, _dry : bool ) -> Result< Self::Report > + { + let mut report = Self::Report::default(); + let package = Package:: + report.base.name = Some( "peter".into() ); + report.base.old_version = Some( self.old_version.to_string() ); + report.changed_files = vec![]; + + Ok( report ) + } + } + + #[ derive( Debug, Default, Clone ) ] + pub struct ExtendedGitReport + { + pub add : Option< process::CmdReport >, + pub commit : Option< process::CmdReport >, + pub push : Option< process::CmdReport >, + } + + pub struct GitThingsPlan + { + pub git_root : AbsolutePath, + pub items : Vec< AbsolutePath >, + pub message : String, + } + + impl Plan for GitThingsPlan + { + type Report = ExtendedGitReport; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let mut report = Self::Report::default(); + if self.items.is_empty() { return Ok( report ); } + let items = self + .items + .iter() + .map + ( + | item | item.as_ref().strip_prefix( self.git_root.as_ref() ).map( Path::to_string_lossy ) + .with_context( || format!( "git_root: {}, item: {}", self.git_root.as_ref().display(), item.as_ref().display() ) ) + ) + .collect::< Result< Vec< _ > > >()?; + let res = git::add( &self.git_root, &items, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.add = Some( res ); + let res = git::commit( &self.git_root, &self.message, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.commit = Some( res ); + let res = git::push( &self.git_root, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.push = Some( res ); + + Ok( report ) + } + } + + pub struct CargoPublishPlan + { + crate_dir : CrateDir, + } + + impl Plan for CargoPublishPlan + { + type Report = process::CmdReport; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let args = cargo::PublishOptions::former() + .path( self.crate_dir.as_ref() ) + .dry( dry ) + .form(); + + Ok( cargo::publish( args )? ) + } + } + + pub struct PublishSinglePackagePlan + { + pub pack : CargoPackagePlan, + pub version_bump : VersionBumpPlan, + // qqq : rename + pub git_things : GitThingsPlan, + pub publish : CargoPublishPlan, + } + + impl Plan for PublishSinglePackagePlan + { + type Report = PublishReport; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let mut report = Self::Report::default(); + let Self + { + pack, + version_bump, + git_things, + publish, + } = self; + + report.get_info = Some( pack.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); + // qqq : redundant field? + report.publish_required = true; + report.bump = Some( version_bump.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); + let git = git_things.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )?; + report.add = git.add; + report.commit = git.commit; + report.push = git.push; + report.publish = Some( publish.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); + + Ok( report ) + } + } + + pub struct PublishManyPackagesPlan( Vec< PublishSinglePackagePlan > ); + + impl Plan for PublishManyPackagesPlan + { + type Report = Vec< PublishReport >; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let mut report = Self::Report::default(); + for package in &self.0 + { + let res = package.perform( dry ).map_err( | e | format_err!( "{report:#?}\n{e:#?}" ) )?; + report.push( res ); + } + + Ok( report ) + } + } + + #[test] + fn temporary_test() { + use core::str::FromStr; + let publish = PublishSinglePackagePlan { + pack: CargoPackagePlan { + crate_dir: AbsolutePath::try_from(".").unwrap().try_into().unwrap(), + }, + version_bump: VersionBumpPlan { + crate_dir: AbsolutePath::try_from(".").unwrap().try_into().unwrap(), + old_version: version::Version::from_str("0.0.1").unwrap(), + new_version: version::Version::from_str("0.0.2").unwrap(), + dependencies: vec![], + }, + git_things: GitThingsPlan { git_root: ".".try_into().unwrap(), items: vec![], message: "hello peter".into() }, + publish: CargoPublishPlan { + crate_dir: AbsolutePath::try_from(".").unwrap().try_into().unwrap(), + } + }; + let many = PublishManyPackagesPlan(vec![publish]); + let out = many.perform(true); + dbg!(out); + panic!() + } + /// Holds information about the publishing process. #[ derive( Debug, Default, Clone ) ] pub struct PublishReport @@ -431,7 +618,7 @@ mod private path } ); - + let pack_args = cargo::PackOptions::former() .path( package_dir.absolute_path().as_ref().to_path_buf() ) .option_temp_path( temp_dir.clone() ) @@ -504,9 +691,9 @@ mod private report.commit = Some( res ); let res = git::push( package_dir, args.dry ).map_err( | e | ( report.clone(), e ) )?; report.push = Some( res ); - + let res = cargo::publish - ( + ( cargo::PublishOptions::former() .path( package_dir.absolute_path().as_ref().to_path_buf() ) .option_temp_path( temp_dir ) diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index 4fb2009d30..50eea06154 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -15,7 +15,7 @@ mod private use manifest::Manifest; /// Wrapper for a SemVer structure - #[ derive( Debug, Clone, Eq, PartialEq ) ] + #[ derive( Debug, Clone, Eq, PartialEq, Ord, PartialOrd ) ] pub struct Version( SemVersion ); impl FromStr for Version diff --git a/module/move/willbe/src/tool/cargo.rs b/module/move/willbe/src/tool/cargo.rs index 6e04399652..0a41976e95 100644 --- a/module/move/willbe/src/tool/cargo.rs +++ b/module/move/willbe/src/tool/cargo.rs @@ -2,21 +2,21 @@ mod private { use std::ffi::OsString; use crate::*; - + use std::path::PathBuf; use former::Former; use process::CmdReport; use wtools::error::Result; /// Represents pack options - #[ derive( Debug, Former ) ] + #[ derive( Debug, Former, Clone ) ] pub struct PackOptions { - path : PathBuf, - temp_path : Option< PathBuf >, - dry : bool, + pub( crate ) path : PathBuf, + pub( crate ) temp_path : Option< PathBuf >, + pub( crate ) dry : bool, } - + impl PackOptionsFormer { pub fn option_temp_path( mut self, value : impl Into< Option< PathBuf > > ) -> Self @@ -25,7 +25,7 @@ mod private self } } - + impl PackOptions { fn to_pack_args( &self ) -> Vec< String > @@ -36,7 +36,7 @@ mod private .collect() } } - + /// /// Assemble the local package into a distributable tarball. /// @@ -84,11 +84,11 @@ mod private #[ derive( Debug, Former, Clone, Default ) ] pub struct PublishOptions { - path : PathBuf, - temp_path : Option< PathBuf >, - dry : bool, + pub( crate ) path : PathBuf, + pub( crate ) temp_path : Option< PathBuf >, + pub( crate ) dry : bool, } - + impl PublishOptionsFormer { pub fn option_temp_path( mut self, value : impl Into< Option< PathBuf > > ) -> Self @@ -133,7 +133,7 @@ mod private } else { - let options = + let options = process::RunOptions::former() .application( program ) .args( arguments.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) diff --git a/module/move/willbe/src/tool/path.rs b/module/move/willbe/src/tool/path.rs index 12ee512322..1cdaf9717f 100644 --- a/module/move/willbe/src/tool/path.rs +++ b/module/move/willbe/src/tool/path.rs @@ -9,6 +9,28 @@ pub( crate ) mod private #[ derive( Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash ) ] pub struct AbsolutePath( PathBuf ); + impl TryFrom< &str > for AbsolutePath + { + type Error = std::io::Error; + + fn try_from( value : &str ) -> Result< Self, Self::Error > + { + let value = PathBuf::from( value ); + Ok( Self( canonicalize( value )? ) ) + } + } + + impl TryFrom< String > for AbsolutePath + { + type Error = std::io::Error; + + fn try_from( value : String ) -> Result< Self, Self::Error > + { + let value = PathBuf::from( value ); + Ok( Self( canonicalize( value )? ) ) + } + } + impl TryFrom< PathBuf > for AbsolutePath { type Error = std::io::Error; @@ -127,7 +149,7 @@ pub( crate ) mod private Ok( path ) } - /// Generate name based on system time + /// Generate name based on system time pub fn unique_folder_name_generate() -> crate::wtools::error::Result< String > { let timestamp = SystemTime::now() From bbc38d111249c72e8eac12e37753267416d0f764 Mon Sep 17 00:00:00 2001 From: Barsik-sus Date: Thu, 14 Mar 2024 20:05:11 +0200 Subject: [PATCH 03/20] wip --- module/move/willbe/src/entity/package.rs | 233 ++++++++++++++++++----- module/move/willbe/src/entity/version.rs | 20 ++ module/move/willbe/tests/inc/mod.rs | 1 + module/move/willbe/tests/inc/package.rs | 159 ++++++++++++++++ 4 files changed, 363 insertions(+), 50 deletions(-) create mode 100644 module/move/willbe/tests/inc/package.rs diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index 0bb6a682e1..d0a7decdfe 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -35,7 +35,7 @@ mod private use former::Former; /// - #[ derive( Debug ) ] + #[ derive( Debug, Clone ) ] pub enum Package { /// `Cargo.toml` file. @@ -202,16 +202,16 @@ mod private match self { Self::Manifest( manifest ) => - { - let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; + { + let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; - // Unwrap safely because of the `Package` type guarantee - Ok( data[ "package" ].get( "repository" ).and_then( | r | r.as_str() ).map( | r | r.to_string()) ) - } + // Unwrap safely because of the `Package` type guarantee + Ok( data[ "package" ].get( "repository" ).and_then( | r | r.as_str() ).map( | r | r.to_string()) ) + } Self::Metadata( metadata ) => - { - Ok( metadata.repository.clone() ) - } + { + Ok( metadata.repository.clone() ) + } } } @@ -221,15 +221,15 @@ mod private match self { Self::Manifest( manifest ) => - { - let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; + { + let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; - Ok( data[ "package" ].get( "metadata" ).and_then( | m | m.get( "discord_url" ) ).and_then( | url | url.as_str() ).map( | r | r.to_string() ) ) - } + Ok( data[ "package" ].get( "metadata" ).and_then( | m | m.get( "discord_url" ) ).and_then( | url | url.as_str() ).map( | r | r.to_string() ) ) + } Self::Metadata( metadata ) => - { - Ok( metadata.metadata[ "discord_url" ].as_str().map( | url | url.to_string() ) ) - } + { + Ok( metadata.metadata[ "discord_url" ].as_str().map( | url | url.to_string() ) ) + } } } @@ -258,8 +258,9 @@ mod private Package::Manifest( manifest ) => Ok( manifest.clone() ), Package::Metadata( metadata ) => manifest::open ( - AbsolutePath::try_from( metadata.manifest_path.as_path() ).map_err( | _ | PackageError::LocalPath )? ) - .map_err( | _ | PackageError::Metadata ), + AbsolutePath::try_from( metadata.manifest_path.as_path() ).map_err( | _ | PackageError::LocalPath )? + ) + .map_err( | _ | PackageError::Metadata ), } } @@ -284,9 +285,11 @@ mod private fn perform( &self, dry : bool ) -> Result< Self::Report >; } + #[ derive( Debug ) ] pub struct CargoPackagePlan { - crate_dir : CrateDir, + pub crate_dir : CrateDir, + pub base_temp_dir : Option< PathBuf >, } impl Plan for CargoPackagePlan @@ -296,6 +299,7 @@ mod private { let args = cargo::PackOptions::former() .path( self.crate_dir.as_ref() ) + .option_temp_path( self.base_temp_dir.clone() ) .dry( dry ) .form(); @@ -303,6 +307,7 @@ mod private } } + #[ derive( Debug ) ] pub struct VersionBumpPlan { pub crate_dir : CrateDir, @@ -314,13 +319,56 @@ mod private impl Plan for VersionBumpPlan { type Report = ExtendedBumpReport; - fn perform( &self, _dry : bool ) -> Result< Self::Report > + fn perform( &self, dry : bool ) -> Result< Self::Report > { let mut report = Self::Report::default(); - let package = Package:: - report.base.name = Some( "peter".into() ); + let package_path = self.crate_dir.absolute_path().join( "Cargo.toml" ); + let package = Package::try_from( package_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let name = package.name().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.base.name = Some( name.clone() ); + let package_version = package.version().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let current_version = version::Version::try_from( package_version.as_str() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + if current_version > self.new_version + { + return Err( format_err!( "{report:?}\nThe current version of the package is higher than need to be set\n\tpackage: {name}\n\tcurrent_version: {current_version}\n\tnew_version: {}", self.new_version ) ); + } report.base.old_version = Some( self.old_version.to_string() ); - report.changed_files = vec![]; + report.base.new_version = Some( self.new_version.to_string() ); + + let mut package_manifest = package.manifest().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + if !dry + { + let data = package_manifest.manifest_data.as_mut().unwrap(); + data[ "package" ][ "version" ] = value( &self.new_version.to_string() ); + package_manifest.store()?; + } + report.changed_files = vec![ package_path ]; + let new_version = &self.new_version.to_string(); + for dep in &self.dependencies + { + let manifest_path = dep.absolute_path().join( "Cargo.toml" ); + let manifest = manifest::open( manifest_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let data = package_manifest.manifest_data.as_mut().unwrap(); + let item = if let Some( item ) = data.get_mut( "package" ) { item } + else if let Some( item ) = data.get_mut( "workspace" ) { item } + else { return Err( format_err!( "{report:?}\nThe manifest nor the package and nor the workspace" ) ); }; + if let Some( dependency ) = item.get_mut( "dependencies" ).and_then( | ds | ds.get_mut( &name ) ) + { + if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) + { + if previous_version.starts_with('~') + { + dependency[ "version" ] = value( format!( "~{new_version}" ) ); + } + else + { + dependency[ "version" ] = value( new_version.clone() ); + } + } + } + if !dry { manifest.store().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; } + report.changed_files.push( manifest_path ); + } Ok( report ) } @@ -334,6 +382,7 @@ mod private pub push : Option< process::CmdReport >, } + #[ derive( Debug ) ] pub struct GitThingsPlan { pub git_root : AbsolutePath, @@ -368,9 +417,11 @@ mod private } } + #[ derive( Debug ) ] pub struct CargoPublishPlan { - crate_dir : CrateDir, + pub crate_dir : CrateDir, + pub base_temp_dir : Option< PathBuf >, } impl Plan for CargoPublishPlan @@ -380,6 +431,7 @@ mod private { let args = cargo::PublishOptions::former() .path( self.crate_dir.as_ref() ) + .option_temp_path( self.base_temp_dir.clone() ) .dry( dry ) .form(); @@ -387,6 +439,7 @@ mod private } } + #[ derive( Debug ) ] pub struct PublishSinglePackagePlan { pub pack : CargoPackagePlan, @@ -396,6 +449,59 @@ mod private pub publish : CargoPublishPlan, } + #[ derive( Debug, Former ) ] + #[ perform( fn build() -> PublishSinglePackagePlan ) ] + pub struct PublishSinglePackagePlanner + { + workspace : Workspace, + package : Package, + base_temp_dir : Option< PathBuf >, + } + + impl PublishSinglePackagePlanner + { + fn build( self ) -> PublishSinglePackagePlan + { + let crate_dir = self.package.crate_dir(); + let workspace_root : AbsolutePath = self.workspace.workspace_root().unwrap().try_into().unwrap(); + let pack = CargoPackagePlan + { + crate_dir : crate_dir.clone(), + base_temp_dir : self.base_temp_dir.clone(), + }; + let old_version : version::Version = self.package.version().as_ref().unwrap().try_into().unwrap(); + let new_version = old_version.clone().bump(); + // bump the package version in dependents (so far, only workspace) + let dependencies = vec![ CrateDir::try_from( workspace_root.clone() ).unwrap() ]; + let version_bump = VersionBumpPlan + { + crate_dir : crate_dir.clone(), + old_version : old_version.clone(), + new_version : new_version.clone(), + dependencies : dependencies.clone(), + }; + let git_things = GitThingsPlan + { + git_root : workspace_root, + items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.absolute_path().join( "Cargo.toml" ) ).collect(), + message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), + }; + let publish = CargoPublishPlan + { + crate_dir, + base_temp_dir : self.base_temp_dir.clone(), + }; + + PublishSinglePackagePlan + { + pack, + version_bump, + git_things, + publish, + } + } + } + impl Plan for PublishSinglePackagePlan { type Report = PublishReport; @@ -424,7 +530,54 @@ mod private } } - pub struct PublishManyPackagesPlan( Vec< PublishSinglePackagePlan > ); + #[ derive( Debug, Former ) ] + pub struct PublishManyPackagesPlan + { + pub workspace : Workspace, + pub base_temp_dir : Option< PathBuf >, + #[ setter( false ) ] + pub plans : Vec< PublishSinglePackagePlan >, + } + + impl PublishManyPackagesPlanFormer + { + pub fn package< IntoPackage >( mut self, package : IntoPackage ) -> Self + where + IntoPackage : Into< Package >, + { + let mut plan = PublishSinglePackagePlanner::former(); + if let Some( workspace ) = &self.container.workspace + { + plan = plan.workspace( workspace.clone() ); + } + if let Some( base_temp_dir ) = &self.container.base_temp_dir + { + plan = plan.base_temp_dir( base_temp_dir.clone() ); + } + let plan = plan + .package( package ) + .perform(); + let mut plans = self.container.plans.unwrap_or_default(); + plans.push( plan ); + + self.container.plans = Some( plans ); + + self + } + + pub fn packages< IntoPackageIter, IntoPackage >( mut self, packages : IntoPackageIter ) -> Self + where + IntoPackageIter : IntoIterator< Item = IntoPackage >, + IntoPackage : Into< Package >, + { + for package in packages + { + self = self.package( package ); + } + + self + } + } impl Plan for PublishManyPackagesPlan { @@ -432,7 +585,7 @@ mod private fn perform( &self, dry : bool ) -> Result< Self::Report > { let mut report = Self::Report::default(); - for package in &self.0 + for package in &self.plans { let res = package.perform( dry ).map_err( | e | format_err!( "{report:#?}\n{e:#?}" ) )?; report.push( res ); @@ -442,30 +595,6 @@ mod private } } - #[test] - fn temporary_test() { - use core::str::FromStr; - let publish = PublishSinglePackagePlan { - pack: CargoPackagePlan { - crate_dir: AbsolutePath::try_from(".").unwrap().try_into().unwrap(), - }, - version_bump: VersionBumpPlan { - crate_dir: AbsolutePath::try_from(".").unwrap().try_into().unwrap(), - old_version: version::Version::from_str("0.0.1").unwrap(), - new_version: version::Version::from_str("0.0.2").unwrap(), - dependencies: vec![], - }, - git_things: GitThingsPlan { git_root: ".".try_into().unwrap(), items: vec![], message: "hello peter".into() }, - publish: CargoPublishPlan { - crate_dir: AbsolutePath::try_from(".").unwrap().try_into().unwrap(), - } - }; - let many = PublishManyPackagesPlan(vec![publish]); - let out = many.perform(true); - dbg!(out); - panic!() - } - /// Holds information about the publishing process. #[ derive( Debug, Default, Clone ) ] pub struct PublishReport @@ -939,6 +1068,10 @@ mod private crate::mod_interface! { + protected use PublishSinglePackagePlanner; + protected use PublishManyPackagesPlan; + protected use Plan; + protected use PublishReport; protected use publish_single; protected use PublishSingleOptions; diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index 50eea06154..aa2e2f16ba 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -28,6 +28,26 @@ mod private } } + impl TryFrom< &str > for Version + { + type Error = semver::Error; + + fn try_from( value : &str ) -> Result< Self, Self::Error > + { + FromStr::from_str( value ) + } + } + + impl TryFrom< &String > for Version + { + type Error = semver::Error; + + fn try_from( value : &String ) -> Result< Self, Self::Error > + { + Self::try_from( value.as_str() ) + } + } + impl fmt::Display for Version { fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result diff --git a/module/move/willbe/tests/inc/mod.rs b/module/move/willbe/tests/inc/mod.rs index 1c460a8bd2..681d5c04a3 100644 --- a/module/move/willbe/tests/inc/mod.rs +++ b/module/move/willbe/tests/inc/mod.rs @@ -3,6 +3,7 @@ use super::*; mod dependencies; mod command; mod action; +mod package; mod publish_need; mod query; mod version; diff --git a/module/move/willbe/tests/inc/package.rs b/module/move/willbe/tests/inc/package.rs new file mode 100644 index 0000000000..cc7d0406dd --- /dev/null +++ b/module/move/willbe/tests/inc/package.rs @@ -0,0 +1,159 @@ +use super::*; +use TheModule:: +{ + Workspace, + path::AbsolutePath, + package::{ Plan, PublishManyPackagesPlan }, +}; + +#[ test ] +fn plan_publish_many_packages() +{ + let workspace = Workspace::from_current_path().unwrap(); + let package = workspace.package_find_by_manifest( /* AbsolutePath::try_from( "../wca/Cargo.toml" ).unwrap() */ ).unwrap().to_owned(); + let mega_plan = PublishManyPackagesPlan::former() + .workspace( workspace ) + .base_temp_dir( "temp" ) + .packages([ package ]) + .form(); + dbg!( &mega_plan.plans ); + // [module/move/willbe/tests/inc/package.rs:19:3] &mega_plan.plans = [ + // PublishSinglePackagePlan { + // pack: CargoPackagePlan { + // crate_dir: CrateDir( + // AbsolutePath( + // ".../wTools/module/move/wca", + // ), + // ), + // base_temp_dir: Some( + // "temp", + // ), + // }, + // version_bump: VersionBumpPlan { + // crate_dir: CrateDir( + // AbsolutePath( + // ".../wTools/module/move/wca", + // ), + // ), + // old_version: Version( + // Version { + // major: 0, + // minor: 12, + // patch: 0, + // }, + // ), + // new_version: Version( + // Version { + // major: 0, + // minor: 13, + // patch: 0, + // }, + // ), + // dependencies: [ + // CrateDir( + // AbsolutePath( + // ".../wTools", + // ), + // ), + // ], + // }, + // git_things: GitThingsPlan { + // git_root: AbsolutePath( + // ".../wTools", + // ), + // items: [ + // AbsolutePath( + // ".../wTools/Cargo.toml", + // ), + // AbsolutePath( + // ".../wTools/module/move/wca/Cargo.toml", + // ), + // ], + // message: "wca-v0.13.0", + // }, + // publish: CargoPublishPlan { + // crate_dir: CrateDir( + // AbsolutePath( + // ".../wTools/module/move/wca", + // ), + // ), + // base_temp_dir: Some( + // "temp", + // ), + // }, + // }, + // ] + let mega_plan = mega_plan.perform( true ); + dbg!( mega_plan ); + // [module/move/willbe/tests/inc/package.rs:21:3] mega_plan = Ok( + // [ + // PublishReport { + // get_info: Some( + // CmdReport { + // command: "cargo package --target-dir temp", + // path: ".../wTools/module/move/wca", + // out: "", + // err: "", + // }, + // ), + // publish_required: true, + // bump: Some( + // ExtendedBumpReport { + // base: BumpReport { + // name: Some( + // "wca", + // ), + // old_version: Some( + // "0.12.0", + // ), + // new_version: Some( + // "0.13.0", + // ), + // }, + // changed_files: [ + // AbsolutePath( + // ".../wTools/module/move/wca/Cargo.toml", + // ), + // AbsolutePath( + // ".../wTools/Cargo.toml", + // ), + // ], + // }, + // ), + // add: Some( + // CmdReport { + // command: "git add Cargo.toml module/move/wca/Cargo.toml", + // path: ".../wTools", + // out: "", + // err: "", + // }, + // ), + // commit: Some( + // CmdReport { + // command: "git commit -m wca-v0.13.0", + // path: ".../wTools", + // out: "", + // err: "", + // }, + // ), + // push: Some( + // CmdReport { + // command: "git push", + // path: ".../wTools", + // out: "", + // err: "", + // }, + // ), + // publish: Some( + // CmdReport { + // command: "cargo publish --target-dir temp", + // path: ".../wTools/module/move/wca", + // out: "", + // err: "", + // }, + // ), + // }, + // ], + // ) + panic!() +} From a2898bc08460501409730e763f133d614e3ada52 Mon Sep 17 00:00:00 2001 From: Barsik Date: Thu, 21 Mar 2024 10:50:14 +0200 Subject: [PATCH 04/20] CRLF -> LF --- module/move/willbe/src/entity/package.rs | 2178 +++++++++++----------- 1 file changed, 1089 insertions(+), 1089 deletions(-) diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index 55fb85702e..dd1e8f5818 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -1,1089 +1,1089 @@ -mod private -{ - use crate::*; - - use std:: - { - path::Path, - collections::{ HashMap, HashSet }, - }; - use std::fmt::Formatter; - use std::hash::Hash; - use std::path::PathBuf; - use cargo_metadata::{ Dependency, DependencyKind, Package as PackageMetadata }; - use toml_edit::value; - - use process_tools::process; - use manifest::{ Manifest, ManifestError }; - use crates_tools::CrateArchive; - - use workspace::Workspace; - use _path::AbsolutePath; - use version::BumpReport; - - use wtools:: - { - iter::Itertools, - error:: - { - thiserror, - Result, - for_lib::Error, - for_app::{ format_err, Error as wError, Context }, - } - }; - use action::readme_health_table_renew::Stability; - use former::Former; - - /// - #[ derive( Debug, Clone ) ] - pub enum Package - { - /// `Cargo.toml` file. - Manifest( Manifest ), - /// Cargo metadata package. - Metadata( PackageMetadata ), - } - - /// Represents errors related to package handling. - #[ derive( Debug, Error ) ] - pub enum PackageError - { - /// Manifest error. - #[ error( "Manifest error. Reason : {0}." ) ] - Manifest( #[ from ] ManifestError ), - /// Fail to load metadata. - #[ error( "Fail to load metadata." ) ] - Metadata, - /// Fail to load remote package. - #[ error( "Fail to load remote package." ) ] - LoadRemotePackage, - /// Fail to get crate local path. - #[ error( "Fail to get crate local path." ) ] - LocalPath, - /// Fail to read archive - #[ error( "Fail to read archive" ) ] - ReadArchive, - /// Try to identify something as a package. - #[ error( "Not a package" ) ] - NotAPackage, - } - - impl TryFrom< AbsolutePath > for Package - { - // qqq : make better errors - // aaa : return `PackageError` instead of `anohow` message - type Error = PackageError; - - fn try_from( value : AbsolutePath ) -> Result< Self, Self::Error > - { - let manifest = manifest::open( value.clone() )?; - if !manifest.package_is()? - { - return Err( PackageError::NotAPackage ); - } - - Ok( Self::Manifest( manifest ) ) - } - } - - impl TryFrom< Manifest > for Package - { - // qqq : make better errors - // aaa : return `PackageError` instead of `anohow` message - type Error = PackageError; - - fn try_from( value : Manifest ) -> Result< Self, Self::Error > - { - if !value.package_is()? - { - return Err( PackageError::NotAPackage ); - } - - Ok( Self::Manifest( value ) ) - } - } - - impl From< PackageMetadata > for Package - { - fn from( value : PackageMetadata ) -> Self - { - Self::Metadata( value ) - } - } - - impl Package - { - /// Path to `Cargo.toml` - pub fn manifest_path( &self ) -> AbsolutePath - { - match self - { - Self::Manifest( manifest ) => manifest.manifest_path.clone(), - Self::Metadata( metadata ) => AbsolutePath::try_from( metadata.manifest_path.as_std_path().to_path_buf() ).unwrap(), - } - } - - /// Path to folder with `Cargo.toml` - pub fn crate_dir( &self ) -> CrateDir - { - match self - { - Self::Manifest( manifest ) => manifest.crate_dir(), - Self::Metadata( metadata ) => - { - let path = metadata.manifest_path.parent().unwrap().as_std_path().to_path_buf(); - let absolute = AbsolutePath::try_from( path ).unwrap(); - - CrateDir::try_from( absolute ).unwrap() - }, - } - } - - /// Package name - pub fn name( &self ) -> Result< String, PackageError > - { - match self - { - Self::Manifest( manifest ) => - { - let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; - - // Unwrap safely because of the `Package` type guarantee - Ok( data[ "package" ][ "name" ].as_str().unwrap().to_string() ) - } - Self::Metadata( metadata ) => - { - Ok( metadata.name.clone() ) - } - } - } - - /// Package version - pub fn version( &self ) -> Result< String, PackageError > - { - match self - { - Self::Manifest( manifest ) => - { - let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; - - // Unwrap safely because of the `Package` type guarantee - Ok( data[ "package" ][ "version" ].as_str().unwrap().to_string() ) - } - Self::Metadata( metadata ) => - { - Ok( metadata.version.to_string() ) - } - } - } - - /// Stability - pub fn stability( &self ) -> Result< Stability, PackageError > - { - match self - { - Self::Manifest( manifest ) => - { - let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; - - // Unwrap safely because of the `Package` type guarantee - Ok( data[ "package" ].get( "metadata" ).and_then( | m | m.get( "stability" ) ).and_then( | s | s.as_str() ).and_then( | s | s.parse::< Stability >().ok() ).unwrap_or( Stability::Experimental) ) - } - Self::Metadata( metadata ) => - { - Ok( metadata.metadata["stability"].as_str().and_then( | s | s.parse::< Stability >().ok() ).unwrap_or( Stability::Experimental) ) - } - } - } - - /// Repository - pub fn repository( &self ) -> Result< Option< String >, PackageError > - { - match self - { - Self::Manifest( manifest ) => - { - let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; - - // Unwrap safely because of the `Package` type guarantee - Ok( data[ "package" ].get( "repository" ).and_then( | r | r.as_str() ).map( | r | r.to_string()) ) - } - Self::Metadata( metadata ) => - { - Ok( metadata.repository.clone() ) - } - } - } - - /// Discord url - pub fn discord_url( &self ) -> Result< Option< String >, PackageError > - { - match self - { - Self::Manifest( manifest ) => - { - let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; - - Ok( data[ "package" ].get( "metadata" ).and_then( | m | m.get( "discord_url" ) ).and_then( | url | url.as_str() ).map( | r | r.to_string() ) ) - } - Self::Metadata( metadata ) => - { - Ok( metadata.metadata[ "discord_url" ].as_str().map( | url | url.to_string() ) ) - } - } - } - - /// Check that module is local. - pub fn local_is( &self ) -> Result< bool, ManifestError > - { - match self - { - Self::Manifest( manifest ) => - { - // verify that manifest not empty - manifest.local_is() - } - Self::Metadata( metadata ) => - { - Ok( !( metadata.publish.is_none() || metadata.publish.as_ref().is_some_and( | p | p.is_empty() ) ) ) - } - } - } - - /// Returns the `Manifest` - pub fn manifest( &self ) -> Result< Manifest, PackageError > - { - match self - { - Package::Manifest( manifest ) => Ok( manifest.clone() ), - Package::Metadata( metadata ) => manifest::open - ( - AbsolutePath::try_from( metadata.manifest_path.as_path() ).map_err( | _ | PackageError::LocalPath )? - ) - .map_err( | _ | PackageError::Metadata ), - } - } - - /// Returns the `Metadata` - pub fn metadata( &self ) -> Result< PackageMetadata, PackageError > - { - match self - { - Package::Manifest( manifest ) => - Workspace::with_crate_dir( manifest.crate_dir() ).map_err( | _ | PackageError::Metadata )? - .package_find_by_manifest( &manifest.manifest_path ) - .ok_or_else( || PackageError::Metadata ) - .cloned(), - Package::Metadata( metadata ) => Ok( metadata.clone() ), - } - } - } - - pub trait Plan - { - type Report; - fn perform( &self, dry : bool ) -> Result< Self::Report >; - } - - #[ derive( Debug ) ] - pub struct CargoPackagePlan - { - pub crate_dir : CrateDir, - pub base_temp_dir : Option< PathBuf >, - } - - impl Plan for CargoPackagePlan - { - type Report = process::Report; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let args = cargo::PackOptions::former() - .path( self.crate_dir.as_ref() ) - .option_temp_path( self.base_temp_dir.clone() ) - .dry( dry ) - .form(); - - Ok( cargo::pack( args )? ) - } - } - - #[ derive( Debug ) ] - pub struct VersionBumpPlan - { - pub crate_dir : CrateDir, - pub old_version : version::Version, - pub new_version : version::Version, - pub dependencies : Vec< CrateDir >, - } - - impl Plan for VersionBumpPlan - { - type Report = ExtendedBumpReport; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let mut report = Self::Report::default(); - let package_path = self.crate_dir.absolute_path().join( "Cargo.toml" ); - let package = Package::try_from( package_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - let name = package.name().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - report.base.name = Some( name.clone() ); - let package_version = package.version().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - let current_version = version::Version::try_from( package_version.as_str() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - if current_version > self.new_version - { - return Err( format_err!( "{report:?}\nThe current version of the package is higher than need to be set\n\tpackage: {name}\n\tcurrent_version: {current_version}\n\tnew_version: {}", self.new_version ) ); - } - report.base.old_version = Some( self.old_version.to_string() ); - report.base.new_version = Some( self.new_version.to_string() ); - - let mut package_manifest = package.manifest().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - if !dry - { - let data = package_manifest.manifest_data.as_mut().unwrap(); - data[ "package" ][ "version" ] = value( &self.new_version.to_string() ); - package_manifest.store()?; - } - report.changed_files = vec![ package_path ]; - let new_version = &self.new_version.to_string(); - for dep in &self.dependencies - { - let manifest_path = dep.absolute_path().join( "Cargo.toml" ); - let manifest = manifest::open( manifest_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - let data = package_manifest.manifest_data.as_mut().unwrap(); - let item = if let Some( item ) = data.get_mut( "package" ) { item } - else if let Some( item ) = data.get_mut( "workspace" ) { item } - else { return Err( format_err!( "{report:?}\nThe manifest nor the package and nor the workspace" ) ); }; - if let Some( dependency ) = item.get_mut( "dependencies" ).and_then( | ds | ds.get_mut( &name ) ) - { - if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) - { - if previous_version.starts_with('~') - { - dependency[ "version" ] = value( format!( "~{new_version}" ) ); - } - else - { - dependency[ "version" ] = value( new_version.clone() ); - } - } - } - if !dry { manifest.store().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; } - report.changed_files.push( manifest_path ); - } - - Ok( report ) - } - } - - #[ derive( Debug, Default, Clone ) ] - pub struct ExtendedGitReport - { - pub add : Option< process::Report >, - pub commit : Option< process::Report >, - pub push : Option< process::Report >, - } - - #[ derive( Debug ) ] - pub struct GitThingsPlan - { - pub git_root : AbsolutePath, - pub items : Vec< AbsolutePath >, - pub message : String, - } - - impl Plan for GitThingsPlan - { - type Report = ExtendedGitReport; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let mut report = Self::Report::default(); - if self.items.is_empty() { return Ok( report ); } - let items = self - .items - .iter() - .map - ( - | item | item.as_ref().strip_prefix( self.git_root.as_ref() ).map( Path::to_string_lossy ) - .with_context( || format!( "git_root: {}, item: {}", self.git_root.as_ref().display(), item.as_ref().display() ) ) - ) - .collect::< Result< Vec< _ > > >()?; - let res = git::add( &self.git_root, &items, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - report.add = Some( res ); - let res = git::commit( &self.git_root, &self.message, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - report.commit = Some( res ); - let res = git::push( &self.git_root, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - report.push = Some( res ); - - Ok( report ) - } - } - - #[ derive( Debug ) ] - pub struct CargoPublishPlan - { - pub crate_dir : CrateDir, - pub base_temp_dir : Option< PathBuf >, - } - - impl Plan for CargoPublishPlan - { - type Report = process::Report; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let args = cargo::PublishOptions::former() - .path( self.crate_dir.as_ref() ) - .option_temp_path( self.base_temp_dir.clone() ) - .dry( dry ) - .form(); - - Ok( cargo::publish( args )? ) - } - } - - #[ derive( Debug ) ] - pub struct PublishSinglePackagePlan - { - pub pack : CargoPackagePlan, - pub version_bump : VersionBumpPlan, - // qqq : rename - pub git_things : GitThingsPlan, - pub publish : CargoPublishPlan, - } - - #[ derive( Debug, Former ) ] - #[ perform( fn build() -> PublishSinglePackagePlan ) ] - pub struct PublishSinglePackagePlanner - { - workspace : Workspace, - package : Package, - base_temp_dir : Option< PathBuf >, - } - - impl PublishSinglePackagePlanner - { - fn build( self ) -> PublishSinglePackagePlan - { - let crate_dir = self.package.crate_dir(); - let workspace_root : AbsolutePath = self.workspace.workspace_root().unwrap().try_into().unwrap(); - let pack = CargoPackagePlan - { - crate_dir : crate_dir.clone(), - base_temp_dir : self.base_temp_dir.clone(), - }; - let old_version : version::Version = self.package.version().as_ref().unwrap().try_into().unwrap(); - let new_version = old_version.clone().bump(); - // bump the package version in dependents (so far, only workspace) - let dependencies = vec![ CrateDir::try_from( workspace_root.clone() ).unwrap() ]; - let version_bump = VersionBumpPlan - { - crate_dir : crate_dir.clone(), - old_version : old_version.clone(), - new_version : new_version.clone(), - dependencies : dependencies.clone(), - }; - let git_things = GitThingsPlan - { - git_root : workspace_root, - items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.absolute_path().join( "Cargo.toml" ) ).collect(), - message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), - }; - let publish = CargoPublishPlan - { - crate_dir, - base_temp_dir : self.base_temp_dir.clone(), - }; - - PublishSinglePackagePlan - { - pack, - version_bump, - git_things, - publish, - } - } - } - - impl Plan for PublishSinglePackagePlan - { - type Report = PublishReport; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let mut report = Self::Report::default(); - let Self - { - pack, - version_bump, - git_things, - publish, - } = self; - - report.get_info = Some( pack.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); - // qqq : redundant field? - report.publish_required = true; - report.bump = Some( version_bump.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); - let git = git_things.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )?; - report.add = git.add; - report.commit = git.commit; - report.push = git.push; - report.publish = Some( publish.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); - - Ok( report ) - } - } - - #[ derive( Debug, Former ) ] - pub struct PublishManyPackagesPlan - { - pub workspace : Workspace, - pub base_temp_dir : Option< PathBuf >, - #[ setter( false ) ] - pub plans : Vec< PublishSinglePackagePlan >, - } - - impl PublishManyPackagesPlanFormer - { - pub fn package< IntoPackage >( mut self, package : IntoPackage ) -> Self - where - IntoPackage : Into< Package >, - { - let mut plan = PublishSinglePackagePlanner::former(); - if let Some( workspace ) = &self.storage.workspace - { - plan = plan.workspace( workspace.clone() ); - } - if let Some( base_temp_dir ) = &self.storage.base_temp_dir - { - plan = plan.base_temp_dir( base_temp_dir.clone() ); - } - let plan = plan - .package( package ) - .perform(); - let mut plans = self.storage.plans.unwrap_or_default(); - plans.push( plan ); - - self.storage.plans = Some( plans ); - - self - } - - pub fn packages< IntoPackageIter, IntoPackage >( mut self, packages : IntoPackageIter ) -> Self - where - IntoPackageIter : IntoIterator< Item = IntoPackage >, - IntoPackage : Into< Package >, - { - for package in packages - { - self = self.package( package ); - } - - self - } - } - - impl Plan for PublishManyPackagesPlan - { - type Report = Vec< PublishReport >; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let mut report = Self::Report::default(); - for package in &self.plans - { - let res = package.perform( dry ).map_err( | e | format_err!( "{report:#?}\n{e:#?}" ) )?; - report.push( res ); - } - - Ok( report ) - } - } - - /// Holds information about the publishing process. - #[ derive( Debug, Default, Clone ) ] - pub struct PublishReport - { - /// Retrieves information about the package. - pub get_info : Option< process::Report >, - /// Indicates whether publishing is required for the package. - pub publish_required : bool, - /// Bumps the version of the package. - pub bump : Option< ExtendedBumpReport >, - /// Report of adding changes to the Git repository. - pub add : Option< process::Report >, - /// Report of committing changes to the Git repository. - pub commit : Option< process::Report >, - /// Report of pushing changes to the Git repository. - pub push : Option< process::Report >, - /// Report of publishes the package using the `cargo publish` command. - pub publish : Option< process::Report >, - } - - impl std::fmt::Display for PublishReport - { - fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result - { - let PublishReport - { - get_info, - publish_required, - bump, - add, - commit, - push, - publish, - } = self; - - if get_info.is_none() - { - f.write_str( "Empty report" )?; - return Ok( () ) - } - let info = get_info.as_ref().unwrap(); - f.write_fmt( format_args!( "{}", info ) )?; - - if !publish_required - { - f.write_str( "The package has no changes, so no publishing is required" )?; - return Ok( () ) - } - - if let Some( bump ) = bump - { - f.write_fmt( format_args!( "{}", bump ) )?; - } - if let Some( add ) = add - { - f.write_fmt( format_args!( "{add}" ) )?; - } - if let Some( commit ) = commit - { - f.write_fmt( format_args!( "{commit}" ) )?; - } - if let Some( push ) = push - { - f.write_fmt( format_args!( "{push}" ) )?; - } - if let Some( publish ) = publish - { - f.write_fmt( format_args!( "{publish}" ) )?; - } - - Ok( () ) - } - } - - /// Report about a changing version. - #[ derive( Debug, Default, Clone ) ] - pub struct ExtendedBumpReport - { - /// Report base. - pub base : BumpReport, - /// Files that should(already) changed for bump. - pub changed_files : Vec< AbsolutePath > - } - - impl std::fmt::Display for ExtendedBumpReport - { - fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result - { - let Self { base, changed_files } = self; - if self.changed_files.is_empty() - { - f.write_str( "Files were not changed during bumping the version" )?; - return Ok( () ) - } - - let files = changed_files.iter().map( | f | f.as_ref().display() ).join( ",\n " ); - f.write_fmt( format_args!( "{base}\n changed files :\n {files}\n" ) )?; - - Ok( () ) - } - } - - /// Option for publish single - #[ derive( Debug, Former ) ] - pub struct PublishSingleOptions< 'a > - { - package : &'a Package, - force : bool, - base_temp_dir : &'a Option< PathBuf >, - dry : bool, - } - - impl < 'a >PublishSingleOptionsFormer< 'a > - { - pub fn option_base_temp_dir( mut self, value : impl Into< &'a Option< PathBuf > > ) -> Self - { - self.storage.base_temp_dir = Some( value.into() ); - self - } - } - - /// Publishes a single package without publishing its dependencies. - /// - /// This function is designed to publish a single package. It does not publish any of the package's dependencies. - /// - /// Args : - /// - /// - package - a package that will be published - /// - dry - a flag that indicates whether to apply the changes or not - /// - true - do not publish, but only show what steps should be taken - /// - false - publishes the package - /// - /// Returns : - /// Returns a result containing a report indicating the result of the operation. - pub fn publish_single< 'a >( args : PublishSingleOptions< 'a > ) -> Result< PublishReport, ( PublishReport, wError ) > - { - let mut report = PublishReport::default(); - if args.package.local_is().map_err( | err | ( report.clone(), format_err!( err ) ) )? - { - return Ok( report ); - } - - let package_dir = &args.package.crate_dir(); - let temp_dir = args.base_temp_dir.as_ref().map - ( - | p | - { - let path = p.join( package_dir.as_ref().file_name().unwrap() ); - std::fs::create_dir_all( &path ).unwrap(); - path - } - ); - - let pack_args = cargo::PackOptions::former() - .path( package_dir.absolute_path().as_ref().to_path_buf() ) - .option_temp_path( temp_dir.clone() ) - .dry( args.dry ) - .form(); - let output = cargo::pack( pack_args ).context( "Take information about package" ).map_err( | e | ( report.clone(), e ) )?; - if output.err.contains( "not yet committed") - { - return Err(( report, format_err!( "Some changes wasn't committed. Please, commit or stash that changes and try again." ) )); - } - report.get_info = Some( output ); - - if args.force || publish_need( &args.package, temp_dir.clone() ).map_err( | err | ( report.clone(), format_err!( err ) ) )? - { - report.publish_required = true; - - let mut files_changed_for_bump = vec![]; - let mut manifest = args.package.manifest().map_err( | err | ( report.clone(), format_err!( err ) ) )?; - // bump a version in the package manifest - let bump_report = version::bump( &mut manifest, args.dry ).context( "Try to bump package version" ).map_err( | e | ( report.clone(), e ) )?; - files_changed_for_bump.push( args.package.manifest_path() ); - let new_version = bump_report.new_version.clone().unwrap(); - - let package_name = args.package.name().map_err( | err | ( report.clone(), format_err!( err ) ) )?; - - // bump the package version in dependents (so far, only workspace) - let workspace_manifest_dir : AbsolutePath = Workspace::with_crate_dir( args.package.crate_dir() ).map_err( | err | ( report.clone(), err ) )?.workspace_root().map_err( | err | ( report.clone(), format_err!( err ) ) )?.try_into().unwrap(); - let workspace_manifest_path = workspace_manifest_dir.join( "Cargo.toml" ); - - // qqq : should be refactored - if !args.dry - { - let mut workspace_manifest = manifest::open( workspace_manifest_path.clone() ).map_err( | e | ( report.clone(), format_err!( e ) ) )?; - let workspace_manifest_data = workspace_manifest.manifest_data.as_mut().ok_or_else( || ( report.clone(), format_err!( PackageError::Manifest( ManifestError::EmptyManifestData ) ) ) )?; - workspace_manifest_data - .get_mut( "workspace" ) - .and_then( | workspace | workspace.get_mut( "dependencies" ) ) - .and_then( | dependencies | dependencies.get_mut( &package_name ) ) - .map - ( - | dependency | - { - if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) - { - if previous_version.starts_with('~') - { - dependency[ "version" ] = value( format!( "~{new_version}" ) ); - } - else - { - dependency[ "version" ] = value( new_version.clone() ); - } - } - } - ) - .unwrap(); - workspace_manifest.store().map_err( | err | ( report.clone(), err.into() ) )?; - } - - files_changed_for_bump.push( workspace_manifest_path ); - let files_changed_for_bump : Vec< _ > = files_changed_for_bump.into_iter().unique().collect(); - let objects_to_add : Vec< _ > = files_changed_for_bump.iter().map( | f | f.as_ref().strip_prefix( &workspace_manifest_dir ).unwrap().to_string_lossy() ).collect(); - - report.bump = Some( ExtendedBumpReport { base : bump_report, changed_files : files_changed_for_bump.clone() } ); - - let commit_message = format!( "{package_name}-v{new_version}" ); - let res = git::add( workspace_manifest_dir, objects_to_add, args.dry ).map_err( | e | ( report.clone(), e ) )?; - report.add = Some( res ); - let res = git::commit( package_dir, commit_message, args.dry ).map_err( | e | ( report.clone(), e ) )?; - report.commit = Some( res ); - let res = git::push( package_dir, args.dry ).map_err( | e | ( report.clone(), e ) )?; - report.push = Some( res ); - - let res = cargo::publish - ( - cargo::PublishOptions::former() - .path( package_dir.absolute_path().as_ref().to_path_buf() ) - .option_temp_path( temp_dir ) - .dry( args.dry ) - .form() - ) - .map_err( | e | ( report.clone(), e ) )?; - report.publish = Some( res ); - } - - Ok( report ) - } - - /// Sorting variants for dependencies. - #[ derive( Debug, Copy, Clone ) ] - pub enum DependenciesSort - { - /// List will be topologically sorted. - Topological, - /// List will be unsorted. - Unordered, - } - - #[ derive( Debug, Clone ) ] - /// Args for `local_dependencies` function. - pub struct DependenciesOptions - { - /// With dependencies of dependencies. - pub recursive : bool, - /// With sorting. - pub sort : DependenciesSort, - /// Include dev dependencies. - pub with_dev : bool, - /// Include remote dependencies. - pub with_remote : bool, - } - - impl Default for DependenciesOptions - { - fn default() -> Self - { - Self - { - recursive : true, - sort : DependenciesSort::Unordered, - with_dev : false, - with_remote : false, - } - } - } - - // - - /// Identifier of any crate(local and remote) - #[ derive( Debug, Clone, Hash, Eq, PartialEq ) ] - pub struct CrateId - { - /// TODO : make it private - pub name : String, - /// TODO : make it private - pub path : Option< AbsolutePath >, - } - - impl From< &PackageMetadata > for CrateId - { - fn from( value : &PackageMetadata ) -> Self - { - Self - { - name : value.name.clone(), - path : Some( AbsolutePath::try_from( value.manifest_path.parent().unwrap() ).unwrap() ), - } - } - } - - impl From< &Dependency > for CrateId - { - fn from( value : &Dependency ) -> Self - { - Self - { - name : value.name.clone(), - path : value.path.clone().map( | path | AbsolutePath::try_from( path ).unwrap() ), - } - } - } - - /// Recursive implementation of the `dependencies` function - pub fn _dependencies - ( - workspace : &mut Workspace, - manifest : &Package, - graph : &mut HashMap< CrateId, HashSet< CrateId > >, - opts : DependenciesOptions - ) -> Result< CrateId > - { - let DependenciesOptions - { - recursive, - sort : _, - with_dev, - with_remote, - } = opts; - if recursive && with_remote { unimplemented!( "`recursive` + `with_remote` options") } - - let manifest_path = &manifest.manifest_path(); - - let package = workspace - .load()? - .package_find_by_manifest( &manifest_path ) - .ok_or( format_err!( "Package not found in the workspace with path : `{}`", manifest_path.as_ref().display() ) )?; - - let deps = package - .dependencies - .iter() - .filter( | dep | ( with_remote || dep.path.is_some() ) && ( with_dev || dep.kind != DependencyKind::Development ) ) - .map( CrateId::from ) - .collect::< HashSet< _ > >(); - - let package = CrateId::from( package ); - graph.insert( package.clone(), deps.clone() ); - - if recursive - { - for dep in deps - { - if graph.get( &dep ).is_none() - { - // unwrap because `recursive` + `with_remote` not yet implemented - _dependencies( workspace, &dep.path.as_ref().unwrap().join( "Cargo.toml" ).try_into().unwrap(), graph, opts.clone() )?; - } - } - } - - Ok( package ) - } - - /// Returns local dependencies of a specified package by its manifest path from a workspace. - /// - /// # Arguments - /// - /// - `workspace` - holds cached information about the workspace, such as the packages it contains and their dependencies. By passing it as a mutable reference, function can update the cache as needed. - /// - `manifest` - The package manifest file contains metadata about the package such as its name, version, and dependencies. - /// - `opts` - used to specify options or configurations for fetching local dependencies. - /// - /// # Returns - /// - /// If the operation is successful, returns a vector of `PathBuf` objects, where each `PathBuf` represents the path to a local dependency of the specified package. - pub fn dependencies( workspace : &mut Workspace, manifest : &Package, opts : DependenciesOptions ) -> Result< Vec< CrateId > > - { - let mut graph = HashMap::new(); - let root = _dependencies( workspace, manifest, &mut graph, opts.clone() )?; - - let output = match opts.sort - { - DependenciesSort::Unordered => - { - graph - .into_iter() - .flat_map( | ( id, dependency ) | - { - dependency - .into_iter() - .chain( Some( id ) ) - }) - .unique() - .filter( | x | x != &root ) - .collect() - } - DependenciesSort::Topological => - { - graph::toposort( graph::construct( &graph ) ).map_err( | err | format_err!( "{}", err ) )?.into_iter().filter( | x | x != &root ).collect() - }, - }; - - Ok( output ) - } - - // - - /// Determines whether a package needs to be published by comparing `.crate` files from the local and remote package. - /// - /// This function requires the local package to be previously packed. - /// - /// # Returns : - /// - `true` if the package needs to be published. - /// - `false` if there is no need to publish the package. - /// - /// Panics if the manifest is not loaded or local package is not packed. - - pub fn publish_need( package : &Package, path : Option< PathBuf > ) -> Result< bool, PackageError > - { - // These files are ignored because they can be safely changed without affecting functionality - // - // - `.cargo_vcs_info.json` - contains the git sha1 hash that varies between different commits - // - `Cargo.toml.orig` - can be safely modified because it is used to generate the `Cargo.toml` file automatically, and the `Cargo.toml` file is sufficient to check for changes - const IGNORE_LIST : [ &str; 2 ] = [ ".cargo_vcs_info.json", "Cargo.toml.orig" ]; - - let name = package.name()?; - let version = package.version()?; - let local_package_path = path - .map( | p | p.join( format!( "package/{0}-{1}.crate", name, version ) ) ) - .unwrap_or( packed_crate::local_path( &name, &version, package.crate_dir() ).map_err( | _ | PackageError::LocalPath )? ); - - // qqq : for Bohdan : bad, properly handle errors - // aaa : return result instead of panic - let local_package = CrateArchive::read( local_package_path ).map_err( | _ | PackageError::ReadArchive )?; - let remote_package = match CrateArchive::download_crates_io( name, version ) - { - Ok( archive ) => archive, - // qqq : fix. we don't have to know about the http status code - Err( ureq::Error::Status( 403, _ ) ) => return Ok( true ), - _ => return Err( PackageError::LoadRemotePackage ), - }; - - let filter_ignore_list = | p : &&Path | !IGNORE_LIST.contains( &p.file_name().unwrap().to_string_lossy().as_ref() ); - let local_package_files : Vec< _ > = local_package.list().into_iter().filter( filter_ignore_list ).sorted().collect(); - let remote_package_files : Vec< _ > = remote_package.list().into_iter().filter( filter_ignore_list ).sorted().collect(); - - if local_package_files != remote_package_files { return Ok( true ); } - - let mut is_same = true; - for path in local_package_files - { - // unwraps is safe because the paths to the files was compared previously - let local = local_package.content_bytes( path ).unwrap(); - let remote = remote_package.content_bytes( path ).unwrap(); - // if local != remote - // { - // println!( "local :\n===\n{}\n===\nremote :\n===\n{}\n===", String::from_utf8_lossy( local ), String::from_utf8_lossy( remote ) ); - // } - - is_same &= local == remote; - } - - Ok( !is_same ) - } - -} - -// - -crate::mod_interface! -{ - - protected use PublishSinglePackagePlanner; - protected use PublishManyPackagesPlan; - protected use Plan; - - protected use PublishReport; - protected use publish_single; - protected use PublishSingleOptions; - protected use Package; - protected use PackageError; - - protected use publish_need; - - protected use CrateId; - protected use DependenciesSort; - protected use DependenciesOptions; - protected use dependencies; - -} +mod private +{ + use crate::*; + + use std:: + { + path::Path, + collections::{ HashMap, HashSet }, + }; + use std::fmt::Formatter; + use std::hash::Hash; + use std::path::PathBuf; + use cargo_metadata::{ Dependency, DependencyKind, Package as PackageMetadata }; + use toml_edit::value; + + use process_tools::process; + use manifest::{ Manifest, ManifestError }; + use crates_tools::CrateArchive; + + use workspace::Workspace; + use _path::AbsolutePath; + use version::BumpReport; + + use wtools:: + { + iter::Itertools, + error:: + { + thiserror, + Result, + for_lib::Error, + for_app::{ format_err, Error as wError, Context }, + } + }; + use action::readme_health_table_renew::Stability; + use former::Former; + + /// + #[ derive( Debug, Clone ) ] + pub enum Package + { + /// `Cargo.toml` file. + Manifest( Manifest ), + /// Cargo metadata package. + Metadata( PackageMetadata ), + } + + /// Represents errors related to package handling. + #[ derive( Debug, Error ) ] + pub enum PackageError + { + /// Manifest error. + #[ error( "Manifest error. Reason : {0}." ) ] + Manifest( #[ from ] ManifestError ), + /// Fail to load metadata. + #[ error( "Fail to load metadata." ) ] + Metadata, + /// Fail to load remote package. + #[ error( "Fail to load remote package." ) ] + LoadRemotePackage, + /// Fail to get crate local path. + #[ error( "Fail to get crate local path." ) ] + LocalPath, + /// Fail to read archive + #[ error( "Fail to read archive" ) ] + ReadArchive, + /// Try to identify something as a package. + #[ error( "Not a package" ) ] + NotAPackage, + } + + impl TryFrom< AbsolutePath > for Package + { + // qqq : make better errors + // aaa : return `PackageError` instead of `anohow` message + type Error = PackageError; + + fn try_from( value : AbsolutePath ) -> Result< Self, Self::Error > + { + let manifest = manifest::open( value.clone() )?; + if !manifest.package_is()? + { + return Err( PackageError::NotAPackage ); + } + + Ok( Self::Manifest( manifest ) ) + } + } + + impl TryFrom< Manifest > for Package + { + // qqq : make better errors + // aaa : return `PackageError` instead of `anohow` message + type Error = PackageError; + + fn try_from( value : Manifest ) -> Result< Self, Self::Error > + { + if !value.package_is()? + { + return Err( PackageError::NotAPackage ); + } + + Ok( Self::Manifest( value ) ) + } + } + + impl From< PackageMetadata > for Package + { + fn from( value : PackageMetadata ) -> Self + { + Self::Metadata( value ) + } + } + + impl Package + { + /// Path to `Cargo.toml` + pub fn manifest_path( &self ) -> AbsolutePath + { + match self + { + Self::Manifest( manifest ) => manifest.manifest_path.clone(), + Self::Metadata( metadata ) => AbsolutePath::try_from( metadata.manifest_path.as_std_path().to_path_buf() ).unwrap(), + } + } + + /// Path to folder with `Cargo.toml` + pub fn crate_dir( &self ) -> CrateDir + { + match self + { + Self::Manifest( manifest ) => manifest.crate_dir(), + Self::Metadata( metadata ) => + { + let path = metadata.manifest_path.parent().unwrap().as_std_path().to_path_buf(); + let absolute = AbsolutePath::try_from( path ).unwrap(); + + CrateDir::try_from( absolute ).unwrap() + }, + } + } + + /// Package name + pub fn name( &self ) -> Result< String, PackageError > + { + match self + { + Self::Manifest( manifest ) => + { + let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; + + // Unwrap safely because of the `Package` type guarantee + Ok( data[ "package" ][ "name" ].as_str().unwrap().to_string() ) + } + Self::Metadata( metadata ) => + { + Ok( metadata.name.clone() ) + } + } + } + + /// Package version + pub fn version( &self ) -> Result< String, PackageError > + { + match self + { + Self::Manifest( manifest ) => + { + let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; + + // Unwrap safely because of the `Package` type guarantee + Ok( data[ "package" ][ "version" ].as_str().unwrap().to_string() ) + } + Self::Metadata( metadata ) => + { + Ok( metadata.version.to_string() ) + } + } + } + + /// Stability + pub fn stability( &self ) -> Result< Stability, PackageError > + { + match self + { + Self::Manifest( manifest ) => + { + let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; + + // Unwrap safely because of the `Package` type guarantee + Ok( data[ "package" ].get( "metadata" ).and_then( | m | m.get( "stability" ) ).and_then( | s | s.as_str() ).and_then( | s | s.parse::< Stability >().ok() ).unwrap_or( Stability::Experimental) ) + } + Self::Metadata( metadata ) => + { + Ok( metadata.metadata["stability"].as_str().and_then( | s | s.parse::< Stability >().ok() ).unwrap_or( Stability::Experimental) ) + } + } + } + + /// Repository + pub fn repository( &self ) -> Result< Option< String >, PackageError > + { + match self + { + Self::Manifest( manifest ) => + { + let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; + + // Unwrap safely because of the `Package` type guarantee + Ok( data[ "package" ].get( "repository" ).and_then( | r | r.as_str() ).map( | r | r.to_string()) ) + } + Self::Metadata( metadata ) => + { + Ok( metadata.repository.clone() ) + } + } + } + + /// Discord url + pub fn discord_url( &self ) -> Result< Option< String >, PackageError > + { + match self + { + Self::Manifest( manifest ) => + { + let data = manifest.manifest_data.as_ref().ok_or_else( || PackageError::Manifest( ManifestError::EmptyManifestData ) )?; + + Ok( data[ "package" ].get( "metadata" ).and_then( | m | m.get( "discord_url" ) ).and_then( | url | url.as_str() ).map( | r | r.to_string() ) ) + } + Self::Metadata( metadata ) => + { + Ok( metadata.metadata[ "discord_url" ].as_str().map( | url | url.to_string() ) ) + } + } + } + + /// Check that module is local. + pub fn local_is( &self ) -> Result< bool, ManifestError > + { + match self + { + Self::Manifest( manifest ) => + { + // verify that manifest not empty + manifest.local_is() + } + Self::Metadata( metadata ) => + { + Ok( !( metadata.publish.is_none() || metadata.publish.as_ref().is_some_and( | p | p.is_empty() ) ) ) + } + } + } + + /// Returns the `Manifest` + pub fn manifest( &self ) -> Result< Manifest, PackageError > + { + match self + { + Package::Manifest( manifest ) => Ok( manifest.clone() ), + Package::Metadata( metadata ) => manifest::open + ( + AbsolutePath::try_from( metadata.manifest_path.as_path() ).map_err( | _ | PackageError::LocalPath )? + ) + .map_err( | _ | PackageError::Metadata ), + } + } + + /// Returns the `Metadata` + pub fn metadata( &self ) -> Result< PackageMetadata, PackageError > + { + match self + { + Package::Manifest( manifest ) => + Workspace::with_crate_dir( manifest.crate_dir() ).map_err( | _ | PackageError::Metadata )? + .package_find_by_manifest( &manifest.manifest_path ) + .ok_or_else( || PackageError::Metadata ) + .cloned(), + Package::Metadata( metadata ) => Ok( metadata.clone() ), + } + } + } + + pub trait Plan + { + type Report; + fn perform( &self, dry : bool ) -> Result< Self::Report >; + } + + #[ derive( Debug ) ] + pub struct CargoPackagePlan + { + pub crate_dir : CrateDir, + pub base_temp_dir : Option< PathBuf >, + } + + impl Plan for CargoPackagePlan + { + type Report = process::Report; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let args = cargo::PackOptions::former() + .path( self.crate_dir.as_ref() ) + .option_temp_path( self.base_temp_dir.clone() ) + .dry( dry ) + .form(); + + Ok( cargo::pack( args )? ) + } + } + + #[ derive( Debug ) ] + pub struct VersionBumpPlan + { + pub crate_dir : CrateDir, + pub old_version : version::Version, + pub new_version : version::Version, + pub dependencies : Vec< CrateDir >, + } + + impl Plan for VersionBumpPlan + { + type Report = ExtendedBumpReport; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let mut report = Self::Report::default(); + let package_path = self.crate_dir.absolute_path().join( "Cargo.toml" ); + let package = Package::try_from( package_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let name = package.name().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.base.name = Some( name.clone() ); + let package_version = package.version().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let current_version = version::Version::try_from( package_version.as_str() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + if current_version > self.new_version + { + return Err( format_err!( "{report:?}\nThe current version of the package is higher than need to be set\n\tpackage: {name}\n\tcurrent_version: {current_version}\n\tnew_version: {}", self.new_version ) ); + } + report.base.old_version = Some( self.old_version.to_string() ); + report.base.new_version = Some( self.new_version.to_string() ); + + let mut package_manifest = package.manifest().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + if !dry + { + let data = package_manifest.manifest_data.as_mut().unwrap(); + data[ "package" ][ "version" ] = value( &self.new_version.to_string() ); + package_manifest.store()?; + } + report.changed_files = vec![ package_path ]; + let new_version = &self.new_version.to_string(); + for dep in &self.dependencies + { + let manifest_path = dep.absolute_path().join( "Cargo.toml" ); + let manifest = manifest::open( manifest_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let data = package_manifest.manifest_data.as_mut().unwrap(); + let item = if let Some( item ) = data.get_mut( "package" ) { item } + else if let Some( item ) = data.get_mut( "workspace" ) { item } + else { return Err( format_err!( "{report:?}\nThe manifest nor the package and nor the workspace" ) ); }; + if let Some( dependency ) = item.get_mut( "dependencies" ).and_then( | ds | ds.get_mut( &name ) ) + { + if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) + { + if previous_version.starts_with('~') + { + dependency[ "version" ] = value( format!( "~{new_version}" ) ); + } + else + { + dependency[ "version" ] = value( new_version.clone() ); + } + } + } + if !dry { manifest.store().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; } + report.changed_files.push( manifest_path ); + } + + Ok( report ) + } + } + + #[ derive( Debug, Default, Clone ) ] + pub struct ExtendedGitReport + { + pub add : Option< process::Report >, + pub commit : Option< process::Report >, + pub push : Option< process::Report >, + } + + #[ derive( Debug ) ] + pub struct GitThingsPlan + { + pub git_root : AbsolutePath, + pub items : Vec< AbsolutePath >, + pub message : String, + } + + impl Plan for GitThingsPlan + { + type Report = ExtendedGitReport; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let mut report = Self::Report::default(); + if self.items.is_empty() { return Ok( report ); } + let items = self + .items + .iter() + .map + ( + | item | item.as_ref().strip_prefix( self.git_root.as_ref() ).map( Path::to_string_lossy ) + .with_context( || format!( "git_root: {}, item: {}", self.git_root.as_ref().display(), item.as_ref().display() ) ) + ) + .collect::< Result< Vec< _ > > >()?; + let res = git::add( &self.git_root, &items, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.add = Some( res ); + let res = git::commit( &self.git_root, &self.message, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.commit = Some( res ); + let res = git::push( &self.git_root, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.push = Some( res ); + + Ok( report ) + } + } + + #[ derive( Debug ) ] + pub struct CargoPublishPlan + { + pub crate_dir : CrateDir, + pub base_temp_dir : Option< PathBuf >, + } + + impl Plan for CargoPublishPlan + { + type Report = process::Report; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let args = cargo::PublishOptions::former() + .path( self.crate_dir.as_ref() ) + .option_temp_path( self.base_temp_dir.clone() ) + .dry( dry ) + .form(); + + Ok( cargo::publish( args )? ) + } + } + + #[ derive( Debug ) ] + pub struct PublishSinglePackagePlan + { + pub pack : CargoPackagePlan, + pub version_bump : VersionBumpPlan, + // qqq : rename + pub git_things : GitThingsPlan, + pub publish : CargoPublishPlan, + } + + #[ derive( Debug, Former ) ] + #[ perform( fn build() -> PublishSinglePackagePlan ) ] + pub struct PublishSinglePackagePlanner + { + workspace : Workspace, + package : Package, + base_temp_dir : Option< PathBuf >, + } + + impl PublishSinglePackagePlanner + { + fn build( self ) -> PublishSinglePackagePlan + { + let crate_dir = self.package.crate_dir(); + let workspace_root : AbsolutePath = self.workspace.workspace_root().unwrap().try_into().unwrap(); + let pack = CargoPackagePlan + { + crate_dir : crate_dir.clone(), + base_temp_dir : self.base_temp_dir.clone(), + }; + let old_version : version::Version = self.package.version().as_ref().unwrap().try_into().unwrap(); + let new_version = old_version.clone().bump(); + // bump the package version in dependents (so far, only workspace) + let dependencies = vec![ CrateDir::try_from( workspace_root.clone() ).unwrap() ]; + let version_bump = VersionBumpPlan + { + crate_dir : crate_dir.clone(), + old_version : old_version.clone(), + new_version : new_version.clone(), + dependencies : dependencies.clone(), + }; + let git_things = GitThingsPlan + { + git_root : workspace_root, + items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.absolute_path().join( "Cargo.toml" ) ).collect(), + message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), + }; + let publish = CargoPublishPlan + { + crate_dir, + base_temp_dir : self.base_temp_dir.clone(), + }; + + PublishSinglePackagePlan + { + pack, + version_bump, + git_things, + publish, + } + } + } + + impl Plan for PublishSinglePackagePlan + { + type Report = PublishReport; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let mut report = Self::Report::default(); + let Self + { + pack, + version_bump, + git_things, + publish, + } = self; + + report.get_info = Some( pack.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); + // qqq : redundant field? + report.publish_required = true; + report.bump = Some( version_bump.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); + let git = git_things.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )?; + report.add = git.add; + report.commit = git.commit; + report.push = git.push; + report.publish = Some( publish.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); + + Ok( report ) + } + } + + #[ derive( Debug, Former ) ] + pub struct PublishManyPackagesPlan + { + pub workspace : Workspace, + pub base_temp_dir : Option< PathBuf >, + #[ setter( false ) ] + pub plans : Vec< PublishSinglePackagePlan >, + } + + impl PublishManyPackagesPlanFormer + { + pub fn package< IntoPackage >( mut self, package : IntoPackage ) -> Self + where + IntoPackage : Into< Package >, + { + let mut plan = PublishSinglePackagePlanner::former(); + if let Some( workspace ) = &self.storage.workspace + { + plan = plan.workspace( workspace.clone() ); + } + if let Some( base_temp_dir ) = &self.storage.base_temp_dir + { + plan = plan.base_temp_dir( base_temp_dir.clone() ); + } + let plan = plan + .package( package ) + .perform(); + let mut plans = self.storage.plans.unwrap_or_default(); + plans.push( plan ); + + self.storage.plans = Some( plans ); + + self + } + + pub fn packages< IntoPackageIter, IntoPackage >( mut self, packages : IntoPackageIter ) -> Self + where + IntoPackageIter : IntoIterator< Item = IntoPackage >, + IntoPackage : Into< Package >, + { + for package in packages + { + self = self.package( package ); + } + + self + } + } + + impl Plan for PublishManyPackagesPlan + { + type Report = Vec< PublishReport >; + fn perform( &self, dry : bool ) -> Result< Self::Report > + { + let mut report = Self::Report::default(); + for package in &self.plans + { + let res = package.perform( dry ).map_err( | e | format_err!( "{report:#?}\n{e:#?}" ) )?; + report.push( res ); + } + + Ok( report ) + } + } + + /// Holds information about the publishing process. + #[ derive( Debug, Default, Clone ) ] + pub struct PublishReport + { + /// Retrieves information about the package. + pub get_info : Option< process::Report >, + /// Indicates whether publishing is required for the package. + pub publish_required : bool, + /// Bumps the version of the package. + pub bump : Option< ExtendedBumpReport >, + /// Report of adding changes to the Git repository. + pub add : Option< process::Report >, + /// Report of committing changes to the Git repository. + pub commit : Option< process::Report >, + /// Report of pushing changes to the Git repository. + pub push : Option< process::Report >, + /// Report of publishes the package using the `cargo publish` command. + pub publish : Option< process::Report >, + } + + impl std::fmt::Display for PublishReport + { + fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + { + let PublishReport + { + get_info, + publish_required, + bump, + add, + commit, + push, + publish, + } = self; + + if get_info.is_none() + { + f.write_str( "Empty report" )?; + return Ok( () ) + } + let info = get_info.as_ref().unwrap(); + f.write_fmt( format_args!( "{}", info ) )?; + + if !publish_required + { + f.write_str( "The package has no changes, so no publishing is required" )?; + return Ok( () ) + } + + if let Some( bump ) = bump + { + f.write_fmt( format_args!( "{}", bump ) )?; + } + if let Some( add ) = add + { + f.write_fmt( format_args!( "{add}" ) )?; + } + if let Some( commit ) = commit + { + f.write_fmt( format_args!( "{commit}" ) )?; + } + if let Some( push ) = push + { + f.write_fmt( format_args!( "{push}" ) )?; + } + if let Some( publish ) = publish + { + f.write_fmt( format_args!( "{publish}" ) )?; + } + + Ok( () ) + } + } + + /// Report about a changing version. + #[ derive( Debug, Default, Clone ) ] + pub struct ExtendedBumpReport + { + /// Report base. + pub base : BumpReport, + /// Files that should(already) changed for bump. + pub changed_files : Vec< AbsolutePath > + } + + impl std::fmt::Display for ExtendedBumpReport + { + fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + { + let Self { base, changed_files } = self; + if self.changed_files.is_empty() + { + f.write_str( "Files were not changed during bumping the version" )?; + return Ok( () ) + } + + let files = changed_files.iter().map( | f | f.as_ref().display() ).join( ",\n " ); + f.write_fmt( format_args!( "{base}\n changed files :\n {files}\n" ) )?; + + Ok( () ) + } + } + + /// Option for publish single + #[ derive( Debug, Former ) ] + pub struct PublishSingleOptions< 'a > + { + package : &'a Package, + force : bool, + base_temp_dir : &'a Option< PathBuf >, + dry : bool, + } + + impl < 'a >PublishSingleOptionsFormer< 'a > + { + pub fn option_base_temp_dir( mut self, value : impl Into< &'a Option< PathBuf > > ) -> Self + { + self.storage.base_temp_dir = Some( value.into() ); + self + } + } + + /// Publishes a single package without publishing its dependencies. + /// + /// This function is designed to publish a single package. It does not publish any of the package's dependencies. + /// + /// Args : + /// + /// - package - a package that will be published + /// - dry - a flag that indicates whether to apply the changes or not + /// - true - do not publish, but only show what steps should be taken + /// - false - publishes the package + /// + /// Returns : + /// Returns a result containing a report indicating the result of the operation. + pub fn publish_single< 'a >( args : PublishSingleOptions< 'a > ) -> Result< PublishReport, ( PublishReport, wError ) > + { + let mut report = PublishReport::default(); + if args.package.local_is().map_err( | err | ( report.clone(), format_err!( err ) ) )? + { + return Ok( report ); + } + + let package_dir = &args.package.crate_dir(); + let temp_dir = args.base_temp_dir.as_ref().map + ( + | p | + { + let path = p.join( package_dir.as_ref().file_name().unwrap() ); + std::fs::create_dir_all( &path ).unwrap(); + path + } + ); + + let pack_args = cargo::PackOptions::former() + .path( package_dir.absolute_path().as_ref().to_path_buf() ) + .option_temp_path( temp_dir.clone() ) + .dry( args.dry ) + .form(); + let output = cargo::pack( pack_args ).context( "Take information about package" ).map_err( | e | ( report.clone(), e ) )?; + if output.err.contains( "not yet committed") + { + return Err(( report, format_err!( "Some changes wasn't committed. Please, commit or stash that changes and try again." ) )); + } + report.get_info = Some( output ); + + if args.force || publish_need( &args.package, temp_dir.clone() ).map_err( | err | ( report.clone(), format_err!( err ) ) )? + { + report.publish_required = true; + + let mut files_changed_for_bump = vec![]; + let mut manifest = args.package.manifest().map_err( | err | ( report.clone(), format_err!( err ) ) )?; + // bump a version in the package manifest + let bump_report = version::bump( &mut manifest, args.dry ).context( "Try to bump package version" ).map_err( | e | ( report.clone(), e ) )?; + files_changed_for_bump.push( args.package.manifest_path() ); + let new_version = bump_report.new_version.clone().unwrap(); + + let package_name = args.package.name().map_err( | err | ( report.clone(), format_err!( err ) ) )?; + + // bump the package version in dependents (so far, only workspace) + let workspace_manifest_dir : AbsolutePath = Workspace::with_crate_dir( args.package.crate_dir() ).map_err( | err | ( report.clone(), err ) )?.workspace_root().map_err( | err | ( report.clone(), format_err!( err ) ) )?.try_into().unwrap(); + let workspace_manifest_path = workspace_manifest_dir.join( "Cargo.toml" ); + + // qqq : should be refactored + if !args.dry + { + let mut workspace_manifest = manifest::open( workspace_manifest_path.clone() ).map_err( | e | ( report.clone(), format_err!( e ) ) )?; + let workspace_manifest_data = workspace_manifest.manifest_data.as_mut().ok_or_else( || ( report.clone(), format_err!( PackageError::Manifest( ManifestError::EmptyManifestData ) ) ) )?; + workspace_manifest_data + .get_mut( "workspace" ) + .and_then( | workspace | workspace.get_mut( "dependencies" ) ) + .and_then( | dependencies | dependencies.get_mut( &package_name ) ) + .map + ( + | dependency | + { + if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) + { + if previous_version.starts_with('~') + { + dependency[ "version" ] = value( format!( "~{new_version}" ) ); + } + else + { + dependency[ "version" ] = value( new_version.clone() ); + } + } + } + ) + .unwrap(); + workspace_manifest.store().map_err( | err | ( report.clone(), err.into() ) )?; + } + + files_changed_for_bump.push( workspace_manifest_path ); + let files_changed_for_bump : Vec< _ > = files_changed_for_bump.into_iter().unique().collect(); + let objects_to_add : Vec< _ > = files_changed_for_bump.iter().map( | f | f.as_ref().strip_prefix( &workspace_manifest_dir ).unwrap().to_string_lossy() ).collect(); + + report.bump = Some( ExtendedBumpReport { base : bump_report, changed_files : files_changed_for_bump.clone() } ); + + let commit_message = format!( "{package_name}-v{new_version}" ); + let res = git::add( workspace_manifest_dir, objects_to_add, args.dry ).map_err( | e | ( report.clone(), e ) )?; + report.add = Some( res ); + let res = git::commit( package_dir, commit_message, args.dry ).map_err( | e | ( report.clone(), e ) )?; + report.commit = Some( res ); + let res = git::push( package_dir, args.dry ).map_err( | e | ( report.clone(), e ) )?; + report.push = Some( res ); + + let res = cargo::publish + ( + cargo::PublishOptions::former() + .path( package_dir.absolute_path().as_ref().to_path_buf() ) + .option_temp_path( temp_dir ) + .dry( args.dry ) + .form() + ) + .map_err( | e | ( report.clone(), e ) )?; + report.publish = Some( res ); + } + + Ok( report ) + } + + /// Sorting variants for dependencies. + #[ derive( Debug, Copy, Clone ) ] + pub enum DependenciesSort + { + /// List will be topologically sorted. + Topological, + /// List will be unsorted. + Unordered, + } + + #[ derive( Debug, Clone ) ] + /// Args for `local_dependencies` function. + pub struct DependenciesOptions + { + /// With dependencies of dependencies. + pub recursive : bool, + /// With sorting. + pub sort : DependenciesSort, + /// Include dev dependencies. + pub with_dev : bool, + /// Include remote dependencies. + pub with_remote : bool, + } + + impl Default for DependenciesOptions + { + fn default() -> Self + { + Self + { + recursive : true, + sort : DependenciesSort::Unordered, + with_dev : false, + with_remote : false, + } + } + } + + // + + /// Identifier of any crate(local and remote) + #[ derive( Debug, Clone, Hash, Eq, PartialEq ) ] + pub struct CrateId + { + /// TODO : make it private + pub name : String, + /// TODO : make it private + pub path : Option< AbsolutePath >, + } + + impl From< &PackageMetadata > for CrateId + { + fn from( value : &PackageMetadata ) -> Self + { + Self + { + name : value.name.clone(), + path : Some( AbsolutePath::try_from( value.manifest_path.parent().unwrap() ).unwrap() ), + } + } + } + + impl From< &Dependency > for CrateId + { + fn from( value : &Dependency ) -> Self + { + Self + { + name : value.name.clone(), + path : value.path.clone().map( | path | AbsolutePath::try_from( path ).unwrap() ), + } + } + } + + /// Recursive implementation of the `dependencies` function + pub fn _dependencies + ( + workspace : &mut Workspace, + manifest : &Package, + graph : &mut HashMap< CrateId, HashSet< CrateId > >, + opts : DependenciesOptions + ) -> Result< CrateId > + { + let DependenciesOptions + { + recursive, + sort : _, + with_dev, + with_remote, + } = opts; + if recursive && with_remote { unimplemented!( "`recursive` + `with_remote` options") } + + let manifest_path = &manifest.manifest_path(); + + let package = workspace + .load()? + .package_find_by_manifest( &manifest_path ) + .ok_or( format_err!( "Package not found in the workspace with path : `{}`", manifest_path.as_ref().display() ) )?; + + let deps = package + .dependencies + .iter() + .filter( | dep | ( with_remote || dep.path.is_some() ) && ( with_dev || dep.kind != DependencyKind::Development ) ) + .map( CrateId::from ) + .collect::< HashSet< _ > >(); + + let package = CrateId::from( package ); + graph.insert( package.clone(), deps.clone() ); + + if recursive + { + for dep in deps + { + if graph.get( &dep ).is_none() + { + // unwrap because `recursive` + `with_remote` not yet implemented + _dependencies( workspace, &dep.path.as_ref().unwrap().join( "Cargo.toml" ).try_into().unwrap(), graph, opts.clone() )?; + } + } + } + + Ok( package ) + } + + /// Returns local dependencies of a specified package by its manifest path from a workspace. + /// + /// # Arguments + /// + /// - `workspace` - holds cached information about the workspace, such as the packages it contains and their dependencies. By passing it as a mutable reference, function can update the cache as needed. + /// - `manifest` - The package manifest file contains metadata about the package such as its name, version, and dependencies. + /// - `opts` - used to specify options or configurations for fetching local dependencies. + /// + /// # Returns + /// + /// If the operation is successful, returns a vector of `PathBuf` objects, where each `PathBuf` represents the path to a local dependency of the specified package. + pub fn dependencies( workspace : &mut Workspace, manifest : &Package, opts : DependenciesOptions ) -> Result< Vec< CrateId > > + { + let mut graph = HashMap::new(); + let root = _dependencies( workspace, manifest, &mut graph, opts.clone() )?; + + let output = match opts.sort + { + DependenciesSort::Unordered => + { + graph + .into_iter() + .flat_map( | ( id, dependency ) | + { + dependency + .into_iter() + .chain( Some( id ) ) + }) + .unique() + .filter( | x | x != &root ) + .collect() + } + DependenciesSort::Topological => + { + graph::toposort( graph::construct( &graph ) ).map_err( | err | format_err!( "{}", err ) )?.into_iter().filter( | x | x != &root ).collect() + }, + }; + + Ok( output ) + } + + // + + /// Determines whether a package needs to be published by comparing `.crate` files from the local and remote package. + /// + /// This function requires the local package to be previously packed. + /// + /// # Returns : + /// - `true` if the package needs to be published. + /// - `false` if there is no need to publish the package. + /// + /// Panics if the manifest is not loaded or local package is not packed. + + pub fn publish_need( package : &Package, path : Option< PathBuf > ) -> Result< bool, PackageError > + { + // These files are ignored because they can be safely changed without affecting functionality + // + // - `.cargo_vcs_info.json` - contains the git sha1 hash that varies between different commits + // - `Cargo.toml.orig` - can be safely modified because it is used to generate the `Cargo.toml` file automatically, and the `Cargo.toml` file is sufficient to check for changes + const IGNORE_LIST : [ &str; 2 ] = [ ".cargo_vcs_info.json", "Cargo.toml.orig" ]; + + let name = package.name()?; + let version = package.version()?; + let local_package_path = path + .map( | p | p.join( format!( "package/{0}-{1}.crate", name, version ) ) ) + .unwrap_or( packed_crate::local_path( &name, &version, package.crate_dir() ).map_err( | _ | PackageError::LocalPath )? ); + + // qqq : for Bohdan : bad, properly handle errors + // aaa : return result instead of panic + let local_package = CrateArchive::read( local_package_path ).map_err( | _ | PackageError::ReadArchive )?; + let remote_package = match CrateArchive::download_crates_io( name, version ) + { + Ok( archive ) => archive, + // qqq : fix. we don't have to know about the http status code + Err( ureq::Error::Status( 403, _ ) ) => return Ok( true ), + _ => return Err( PackageError::LoadRemotePackage ), + }; + + let filter_ignore_list = | p : &&Path | !IGNORE_LIST.contains( &p.file_name().unwrap().to_string_lossy().as_ref() ); + let local_package_files : Vec< _ > = local_package.list().into_iter().filter( filter_ignore_list ).sorted().collect(); + let remote_package_files : Vec< _ > = remote_package.list().into_iter().filter( filter_ignore_list ).sorted().collect(); + + if local_package_files != remote_package_files { return Ok( true ); } + + let mut is_same = true; + for path in local_package_files + { + // unwraps is safe because the paths to the files was compared previously + let local = local_package.content_bytes( path ).unwrap(); + let remote = remote_package.content_bytes( path ).unwrap(); + // if local != remote + // { + // println!( "local :\n===\n{}\n===\nremote :\n===\n{}\n===", String::from_utf8_lossy( local ), String::from_utf8_lossy( remote ) ); + // } + + is_same &= local == remote; + } + + Ok( !is_same ) + } + +} + +// + +crate::mod_interface! +{ + + protected use PublishSinglePackagePlanner; + protected use PublishManyPackagesPlan; + protected use Plan; + + protected use PublishReport; + protected use publish_single; + protected use PublishSingleOptions; + protected use Package; + protected use PackageError; + + protected use publish_need; + + protected use CrateId; + protected use DependenciesSort; + protected use DependenciesOptions; + protected use dependencies; + +} From 74742926683b4641fc4c37848acff2fd6f7a2ede Mon Sep 17 00:00:00 2001 From: Barsik Date: Thu, 21 Mar 2024 11:07:21 +0200 Subject: [PATCH 05/20] Refactor plan structures to options and instructions We have renamed multiple structures in the package and test files. This was done to better reflect the functionality of these structures, replacing "Plan" suffixes with more appropriate ones such as "Options" and "Instructions". This refactoring should make the code easier to understand and more intuitive to use. --- module/move/willbe/src/entity/package.rs | 52 ++++++++++++------------ module/move/willbe/tests/inc/package.rs | 8 ++-- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index dd1e8f5818..d08c98cced 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -287,13 +287,13 @@ mod private } #[ derive( Debug ) ] - pub struct CargoPackagePlan + pub struct CargoPackageOptions { pub crate_dir : CrateDir, pub base_temp_dir : Option< PathBuf >, } - impl Plan for CargoPackagePlan + impl Plan for CargoPackageOptions { type Report = process::Report; fn perform( &self, dry : bool ) -> Result< Self::Report > @@ -309,7 +309,7 @@ mod private } #[ derive( Debug ) ] - pub struct VersionBumpPlan + pub struct VersionBumpOptions { pub crate_dir : CrateDir, pub old_version : version::Version, @@ -317,7 +317,7 @@ mod private pub dependencies : Vec< CrateDir >, } - impl Plan for VersionBumpPlan + impl Plan for VersionBumpOptions { type Report = ExtendedBumpReport; fn perform( &self, dry : bool ) -> Result< Self::Report > @@ -384,14 +384,14 @@ mod private } #[ derive( Debug ) ] - pub struct GitThingsPlan + pub struct GitThingsOptions { pub git_root : AbsolutePath, pub items : Vec< AbsolutePath >, pub message : String, } - impl Plan for GitThingsPlan + impl Plan for GitThingsOptions { type Report = ExtendedGitReport; fn perform( &self, dry : bool ) -> Result< Self::Report > @@ -419,13 +419,13 @@ mod private } #[ derive( Debug ) ] - pub struct CargoPublishPlan + pub struct CargoPublishOptions { pub crate_dir : CrateDir, pub base_temp_dir : Option< PathBuf >, } - impl Plan for CargoPublishPlan + impl Plan for CargoPublishOptions { type Report = process::Report; fn perform( &self, dry : bool ) -> Result< Self::Report > @@ -441,17 +441,17 @@ mod private } #[ derive( Debug ) ] - pub struct PublishSinglePackagePlan + pub struct PackagePublishInstruction { - pub pack : CargoPackagePlan, - pub version_bump : VersionBumpPlan, + pub pack : CargoPackageOptions, + pub version_bump : VersionBumpOptions, // qqq : rename - pub git_things : GitThingsPlan, - pub publish : CargoPublishPlan, + pub git_things : GitThingsOptions, + pub publish : CargoPublishOptions, } #[ derive( Debug, Former ) ] - #[ perform( fn build() -> PublishSinglePackagePlan ) ] + #[ perform( fn build() -> PackagePublishInstruction ) ] pub struct PublishSinglePackagePlanner { workspace : Workspace, @@ -461,11 +461,11 @@ mod private impl PublishSinglePackagePlanner { - fn build( self ) -> PublishSinglePackagePlan + fn build( self ) -> PackagePublishInstruction { let crate_dir = self.package.crate_dir(); let workspace_root : AbsolutePath = self.workspace.workspace_root().unwrap().try_into().unwrap(); - let pack = CargoPackagePlan + let pack = CargoPackageOptions { crate_dir : crate_dir.clone(), base_temp_dir : self.base_temp_dir.clone(), @@ -474,26 +474,26 @@ mod private let new_version = old_version.clone().bump(); // bump the package version in dependents (so far, only workspace) let dependencies = vec![ CrateDir::try_from( workspace_root.clone() ).unwrap() ]; - let version_bump = VersionBumpPlan + let version_bump = VersionBumpOptions { crate_dir : crate_dir.clone(), old_version : old_version.clone(), new_version : new_version.clone(), dependencies : dependencies.clone(), }; - let git_things = GitThingsPlan + let git_things = GitThingsOptions { git_root : workspace_root, items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.absolute_path().join( "Cargo.toml" ) ).collect(), message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), }; - let publish = CargoPublishPlan + let publish = CargoPublishOptions { crate_dir, base_temp_dir : self.base_temp_dir.clone(), }; - PublishSinglePackagePlan + PackagePublishInstruction { pack, version_bump, @@ -503,7 +503,7 @@ mod private } } - impl Plan for PublishSinglePackagePlan + impl Plan for PackagePublishInstruction { type Report = PublishReport; fn perform( &self, dry : bool ) -> Result< Self::Report > @@ -532,15 +532,15 @@ mod private } #[ derive( Debug, Former ) ] - pub struct PublishManyPackagesPlan + pub struct PublishPlan { pub workspace : Workspace, pub base_temp_dir : Option< PathBuf >, #[ setter( false ) ] - pub plans : Vec< PublishSinglePackagePlan >, + pub plans : Vec< PackagePublishInstruction >, } - impl PublishManyPackagesPlanFormer + impl PublishPlanFormer { pub fn package< IntoPackage >( mut self, package : IntoPackage ) -> Self where @@ -580,7 +580,7 @@ mod private } } - impl Plan for PublishManyPackagesPlan + impl Plan for PublishPlan { type Report = Vec< PublishReport >; fn perform( &self, dry : bool ) -> Result< Self::Report > @@ -1070,7 +1070,7 @@ crate::mod_interface! { protected use PublishSinglePackagePlanner; - protected use PublishManyPackagesPlan; + protected use PublishPlan; protected use Plan; protected use PublishReport; diff --git a/module/move/willbe/tests/inc/package.rs b/module/move/willbe/tests/inc/package.rs index cc7d0406dd..3114fc3177 100644 --- a/module/move/willbe/tests/inc/package.rs +++ b/module/move/willbe/tests/inc/package.rs @@ -1,9 +1,9 @@ use super::*; -use TheModule:: +use the_module:: { Workspace, - path::AbsolutePath, - package::{ Plan, PublishManyPackagesPlan }, + _path::AbsolutePath, + package::{Plan, PublishPlan}, }; #[ test ] @@ -11,7 +11,7 @@ fn plan_publish_many_packages() { let workspace = Workspace::from_current_path().unwrap(); let package = workspace.package_find_by_manifest( /* AbsolutePath::try_from( "../wca/Cargo.toml" ).unwrap() */ ).unwrap().to_owned(); - let mega_plan = PublishManyPackagesPlan::former() + let mega_plan = PublishPlan::former() .workspace( workspace ) .base_temp_dir( "temp" ) .packages([ package ]) From 0a840d26981dee88d098780deb6404527503b28e Mon Sep 17 00:00:00 2001 From: Barsik Date: Thu, 21 Mar 2024 12:12:09 +0200 Subject: [PATCH 06/20] Refactor Plan trait to individual functions for version bumping, git operations, and package publishing The Plan trait was replaced with specific functions for version bumping, git operations, and package publishing. The functions `version_bump`, `perform_git_operations`, and `perform_package_publish` were briefed to run these specific tasks. --- module/move/willbe/src/entity/package.rs | 306 +++++++---------------- module/move/willbe/src/entity/version.rs | 119 +++++++++ module/move/willbe/tests/inc/package.rs | 289 +++++++++++---------- 3 files changed, 361 insertions(+), 353 deletions(-) diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index d08c98cced..acd5bc478d 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -280,100 +280,6 @@ mod private } } - pub trait Plan - { - type Report; - fn perform( &self, dry : bool ) -> Result< Self::Report >; - } - - #[ derive( Debug ) ] - pub struct CargoPackageOptions - { - pub crate_dir : CrateDir, - pub base_temp_dir : Option< PathBuf >, - } - - impl Plan for CargoPackageOptions - { - type Report = process::Report; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let args = cargo::PackOptions::former() - .path( self.crate_dir.as_ref() ) - .option_temp_path( self.base_temp_dir.clone() ) - .dry( dry ) - .form(); - - Ok( cargo::pack( args )? ) - } - } - - #[ derive( Debug ) ] - pub struct VersionBumpOptions - { - pub crate_dir : CrateDir, - pub old_version : version::Version, - pub new_version : version::Version, - pub dependencies : Vec< CrateDir >, - } - - impl Plan for VersionBumpOptions - { - type Report = ExtendedBumpReport; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let mut report = Self::Report::default(); - let package_path = self.crate_dir.absolute_path().join( "Cargo.toml" ); - let package = Package::try_from( package_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - let name = package.name().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - report.base.name = Some( name.clone() ); - let package_version = package.version().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - let current_version = version::Version::try_from( package_version.as_str() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - if current_version > self.new_version - { - return Err( format_err!( "{report:?}\nThe current version of the package is higher than need to be set\n\tpackage: {name}\n\tcurrent_version: {current_version}\n\tnew_version: {}", self.new_version ) ); - } - report.base.old_version = Some( self.old_version.to_string() ); - report.base.new_version = Some( self.new_version.to_string() ); - - let mut package_manifest = package.manifest().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - if !dry - { - let data = package_manifest.manifest_data.as_mut().unwrap(); - data[ "package" ][ "version" ] = value( &self.new_version.to_string() ); - package_manifest.store()?; - } - report.changed_files = vec![ package_path ]; - let new_version = &self.new_version.to_string(); - for dep in &self.dependencies - { - let manifest_path = dep.absolute_path().join( "Cargo.toml" ); - let manifest = manifest::open( manifest_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - let data = package_manifest.manifest_data.as_mut().unwrap(); - let item = if let Some( item ) = data.get_mut( "package" ) { item } - else if let Some( item ) = data.get_mut( "workspace" ) { item } - else { return Err( format_err!( "{report:?}\nThe manifest nor the package and nor the workspace" ) ); }; - if let Some( dependency ) = item.get_mut( "dependencies" ).and_then( | ds | ds.get_mut( &name ) ) - { - if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) - { - if previous_version.starts_with('~') - { - dependency[ "version" ] = value( format!( "~{new_version}" ) ); - } - else - { - dependency[ "version" ] = value( new_version.clone() ); - } - } - } - if !dry { manifest.store().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; } - report.changed_files.push( manifest_path ); - } - - Ok( report ) - } - } #[ derive( Debug, Default, Clone ) ] pub struct ExtendedGitReport @@ -389,65 +295,41 @@ mod private pub git_root : AbsolutePath, pub items : Vec< AbsolutePath >, pub message : String, + pub dry : bool, } - impl Plan for GitThingsOptions - { - type Report = ExtendedGitReport; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let mut report = Self::Report::default(); - if self.items.is_empty() { return Ok( report ); } - let items = self - .items - .iter() - .map - ( - | item | item.as_ref().strip_prefix( self.git_root.as_ref() ).map( Path::to_string_lossy ) - .with_context( || format!( "git_root: {}, item: {}", self.git_root.as_ref().display(), item.as_ref().display() ) ) - ) - .collect::< Result< Vec< _ > > >()?; - let res = git::add( &self.git_root, &items, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - report.add = Some( res ); - let res = git::commit( &self.git_root, &self.message, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - report.commit = Some( res ); - let res = git::push( &self.git_root, dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - report.push = Some( res ); - - Ok( report ) - } - } - - #[ derive( Debug ) ] - pub struct CargoPublishOptions + fn perform_git_operations(args : GitThingsOptions ) -> Result< ExtendedGitReport > { - pub crate_dir : CrateDir, - pub base_temp_dir : Option< PathBuf >, - } - - impl Plan for CargoPublishOptions - { - type Report = process::Report; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let args = cargo::PublishOptions::former() - .path( self.crate_dir.as_ref() ) - .option_temp_path( self.base_temp_dir.clone() ) - .dry( dry ) - .form(); + let mut report = ExtendedGitReport::default(); + if args.items.is_empty() { return Ok( report ); } + let items = args + .items + .iter() + .map + ( + | item | item.as_ref().strip_prefix( args.git_root.as_ref() ).map( Path::to_string_lossy ) + .with_context( || format!( "git_root: {}, item: {}", args.git_root.as_ref().display(), item.as_ref().display() ) ) + ) + .collect::< Result< Vec< _ > > >()?; + let res = git::add( &args.git_root, &items, args.dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.add = Some( res ); + let res = git::commit( &args.git_root, &args.message, args.dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.commit = Some( res ); + let res = git::push( &args.git_root, args.dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.push = Some( res ); - Ok( cargo::publish( args )? ) - } + Ok( report ) } #[ derive( Debug ) ] pub struct PackagePublishInstruction { - pub pack : CargoPackageOptions, - pub version_bump : VersionBumpOptions, + pub pack : cargo::PackOptions, + pub version_bump : version::BumpOptions, // qqq : rename pub git_things : GitThingsOptions, - pub publish : CargoPublishOptions, + pub publish : cargo::PublishOptions, + pub dry : bool, } #[ derive( Debug, Former ) ] @@ -465,32 +347,36 @@ mod private { let crate_dir = self.package.crate_dir(); let workspace_root : AbsolutePath = self.workspace.workspace_root().unwrap().try_into().unwrap(); - let pack = CargoPackageOptions + let pack = cargo::PackOptions { - crate_dir : crate_dir.clone(), - base_temp_dir : self.base_temp_dir.clone(), + path : crate_dir.as_ref().into(), + temp_path : self.base_temp_dir.clone(), + dry : true, }; let old_version : version::Version = self.package.version().as_ref().unwrap().try_into().unwrap(); let new_version = old_version.clone().bump(); // bump the package version in dependents (so far, only workspace) let dependencies = vec![ CrateDir::try_from( workspace_root.clone() ).unwrap() ]; - let version_bump = VersionBumpOptions + let version_bump = version::BumpOptions { crate_dir : crate_dir.clone(), old_version : old_version.clone(), new_version : new_version.clone(), dependencies : dependencies.clone(), + dry : true, }; let git_things = GitThingsOptions { git_root : workspace_root, items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.absolute_path().join( "Cargo.toml" ) ).collect(), message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), + dry : true, }; - let publish = CargoPublishOptions + let publish = cargo::PublishOptions { - crate_dir, - base_temp_dir : self.base_temp_dir.clone(), + path : crate_dir.as_ref().into(), + temp_path : self.base_temp_dir.clone(), + dry : true, }; PackagePublishInstruction @@ -499,36 +385,47 @@ mod private version_bump, git_things, publish, + dry : true, } } } - impl Plan for PackagePublishInstruction + /// Performs package publishing based on the given arguments. + /// + /// # Arguments + /// + /// * `args` - The package publishing instructions. + /// + /// # Returns + /// + /// * `Result` - The result of the publishing operation, including information about the publish, version bump, and git operations. + pub fn perform_package_publish( args : PackagePublishInstruction ) -> Result< PublishReport > { - type Report = PublishReport; - fn perform( &self, dry : bool ) -> Result< Self::Report > - { - let mut report = Self::Report::default(); - let Self - { - pack, - version_bump, - git_things, - publish, - } = self; + let mut report = PublishReport::default(); + let PackagePublishInstruction + { + mut pack, + mut version_bump, + mut git_things, + mut publish, + dry, + } = args; + pack.dry = dry; + version_bump.dry = dry; + git_things.dry = dry; + publish.dry = dry; + + report.get_info = Some( cargo::pack( pack ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); + // qqq : redundant field? + report.publish_required = true; + report.bump = Some( version::version_bump( version_bump ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); + let git = perform_git_operations( git_things ).map_err( |e | format_err!( "{report}\n{e:#?}" ) )?; + report.add = git.add; + report.commit = git.commit; + report.push = git.push; + report.publish = Some( cargo::publish( publish ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); - report.get_info = Some( pack.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); - // qqq : redundant field? - report.publish_required = true; - report.bump = Some( version_bump.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); - let git = git_things.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )?; - report.add = git.add; - report.commit = git.commit; - report.push = git.push; - report.publish = Some( publish.perform( dry ).map_err( | e | format_err!( "{report}\n{e:#?}" ) )? ); - - Ok( report ) - } + Ok( report ) } #[ derive( Debug, Former ) ] @@ -580,20 +477,26 @@ mod private } } - impl Plan for PublishPlan + + /// Perform publishing of multiple packages based on the provided publish plan. + /// + /// # Arguments + /// + /// * `plan` - The publish plan with details of packages to be published. + /// + /// # Returns + /// + /// Returns a `Result` containing a vector of `PublishReport` if successful, else an error. + pub fn perform_packages_publish( plan : PublishPlan ) -> Result< Vec< PublishReport > > { - type Report = Vec< PublishReport >; - fn perform( &self, dry : bool ) -> Result< Self::Report > + let mut report = vec![]; + for package in plan.plans { - let mut report = Self::Report::default(); - for package in &self.plans - { - let res = package.perform( dry ).map_err( | e | format_err!( "{report:#?}\n{e:#?}" ) )?; - report.push( res ); - } - - Ok( report ) + let res = perform_package_publish( package ).map_err( | e | format_err!( "{report:#?}\n{e:#?}" ) )?; + report.push( res ); } + + Ok( report ) } /// Holds information about the publishing process. @@ -605,7 +508,7 @@ mod private /// Indicates whether publishing is required for the package. pub publish_required : bool, /// Bumps the version of the package. - pub bump : Option< ExtendedBumpReport >, + pub bump : Option< version::ExtendedBumpReport >, /// Report of adding changes to the Git repository. pub add : Option< process::Report >, /// Report of committing changes to the Git repository. @@ -670,34 +573,6 @@ mod private } } - /// Report about a changing version. - #[ derive( Debug, Default, Clone ) ] - pub struct ExtendedBumpReport - { - /// Report base. - pub base : BumpReport, - /// Files that should(already) changed for bump. - pub changed_files : Vec< AbsolutePath > - } - - impl std::fmt::Display for ExtendedBumpReport - { - fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result - { - let Self { base, changed_files } = self; - if self.changed_files.is_empty() - { - f.write_str( "Files were not changed during bumping the version" )?; - return Ok( () ) - } - - let files = changed_files.iter().map( | f | f.as_ref().display() ).join( ",\n " ); - f.write_fmt( format_args!( "{base}\n changed files :\n {files}\n" ) )?; - - Ok( () ) - } - } - /// Option for publish single #[ derive( Debug, Former ) ] pub struct PublishSingleOptions< 'a > @@ -812,7 +687,7 @@ mod private let files_changed_for_bump : Vec< _ > = files_changed_for_bump.into_iter().unique().collect(); let objects_to_add : Vec< _ > = files_changed_for_bump.iter().map( | f | f.as_ref().strip_prefix( &workspace_manifest_dir ).unwrap().to_string_lossy() ).collect(); - report.bump = Some( ExtendedBumpReport { base : bump_report, changed_files : files_changed_for_bump.clone() } ); + report.bump = Some( version::ExtendedBumpReport { base : bump_report, changed_files : files_changed_for_bump.clone() } ); let commit_message = format!( "{package_name}-v{new_version}" ); let res = git::add( workspace_manifest_dir, objects_to_add, args.dry ).map_err( | e | ( report.clone(), e ) )?; @@ -1071,7 +946,8 @@ crate::mod_interface! protected use PublishSinglePackagePlanner; protected use PublishPlan; - protected use Plan; + protected use perform_package_publish; + protected use perform_packages_publish; protected use PublishReport; protected use publish_single; diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index ca2888b5e3..cb08ce7a8e 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -8,11 +8,15 @@ mod private fmt, str::FromStr, }; + use std::fmt::Formatter; use toml_edit::value; use semver::Version as SemVersion; use wtools::error::for_app::Result; use manifest::Manifest; + use _path::AbsolutePath; + use package::Package; + use wtools::{ error::anyhow::format_err, iter::Itertools }; /// Wrapper for a SemVer structure #[ derive( Debug, Clone, Eq, PartialEq, Ord, PartialOrd ) ] @@ -164,6 +168,114 @@ mod private Ok( report ) } + + // qqq : we have to replace the implementation above with the implementation below, don't we? + + /// Options for version bumping. + /// + /// This struct is used to specify the options for version bumping operations. + #[ derive( Debug ) ] + pub struct BumpOptions + { + pub crate_dir : CrateDir, + pub old_version : Version, + pub new_version : Version, + pub dependencies : Vec< CrateDir >, + pub dry : bool, + } + + /// Report about a changing version. + #[ derive( Debug, Default, Clone ) ] + pub struct ExtendedBumpReport + { + /// Report base. + pub base : BumpReport, + /// Files that should(already) changed for bump. + pub changed_files : Vec< AbsolutePath > + } + + impl std::fmt::Display for ExtendedBumpReport + { + fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + { + let Self { base, changed_files } = self; + if self.changed_files.is_empty() + { + f.write_str( "Files were not changed during bumping the version" )?; + return Ok( () ) + } + + let files = changed_files.iter().map( | f | f.as_ref().display() ).join( ",\n " ); + f.write_fmt( format_args!( "{base}\n changed files :\n {files}\n" ) )?; + + Ok( () ) + } + } + + + /// Bumps the version of a package and its dependencies. + /// + /// # Arguments + /// + /// * `args` - The options for version bumping. + /// + /// # Returns + /// + /// Returns a result containing the extended bump report if successful. + /// + pub fn version_bump( args : BumpOptions ) -> Result< ExtendedBumpReport > + { + let mut report = ExtendedBumpReport::default(); + let package_path = args.crate_dir.absolute_path().join( "Cargo.toml" ); + let package = Package::try_from( package_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let name = package.name().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + report.base.name = Some( name.clone() ); + let package_version = package.version().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let current_version = version::Version::try_from( package_version.as_str() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + if current_version > args.new_version + { + return Err( format_err!( "{report:?}\nThe current version of the package is higher than need to be set\n\tpackage: {name}\n\tcurrent_version: {current_version}\n\tnew_version: {}", args.new_version ) ); + } + report.base.old_version = Some( args.old_version.to_string() ); + report.base.new_version = Some( args.new_version.to_string() ); + + let mut package_manifest = package.manifest().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + if !args.dry + { + let data = package_manifest.manifest_data.as_mut().unwrap(); + data[ "package" ][ "version" ] = value( &args.new_version.to_string() ); + package_manifest.store()?; + } + report.changed_files = vec![ package_path ]; + let new_version = &args.new_version.to_string(); + for dep in &args.dependencies + { + let manifest_path = dep.absolute_path().join( "Cargo.toml" ); + let manifest = manifest::open( manifest_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let data = package_manifest.manifest_data.as_mut().unwrap(); + let item = if let Some( item ) = data.get_mut( "package" ) { item } + else if let Some( item ) = data.get_mut( "workspace" ) { item } + else { return Err( format_err!( "{report:?}\nThe manifest nor the package and nor the workspace" ) ); }; + if let Some( dependency ) = item.get_mut( "dependencies" ).and_then( | ds | ds.get_mut( &name ) ) + { + if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) + { + if previous_version.starts_with('~') + { + dependency[ "version" ] = value( format!( "~{new_version}" ) ); + } + else + { + dependency[ "version" ] = value( new_version.clone() ); + } + } + } + if !args.dry { manifest.store().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; } + report.changed_files.push( manifest_path ); + } + + Ok( report ) + } } // @@ -178,4 +290,11 @@ crate::mod_interface! /// Bump version. protected use bump; + + /// Options for version bumping. + protected use BumpOptions; + /// Report about a changing version with list of files that was changed. + protected use ExtendedBumpReport; + /// Bumps the version of a package and its dependencies. + protected use version_bump; } diff --git a/module/move/willbe/tests/inc/package.rs b/module/move/willbe/tests/inc/package.rs index 3114fc3177..5e99c78fec 100644 --- a/module/move/willbe/tests/inc/package.rs +++ b/module/move/willbe/tests/inc/package.rs @@ -3,8 +3,9 @@ use the_module:: { Workspace, _path::AbsolutePath, - package::{Plan, PublishPlan}, + package::PublishPlan, }; +use willbe::package::perform_packages_publish; #[ test ] fn plan_publish_many_packages() @@ -17,143 +18,155 @@ fn plan_publish_many_packages() .packages([ package ]) .form(); dbg!( &mega_plan.plans ); - // [module/move/willbe/tests/inc/package.rs:19:3] &mega_plan.plans = [ - // PublishSinglePackagePlan { - // pack: CargoPackagePlan { - // crate_dir: CrateDir( - // AbsolutePath( - // ".../wTools/module/move/wca", - // ), - // ), - // base_temp_dir: Some( - // "temp", - // ), - // }, - // version_bump: VersionBumpPlan { - // crate_dir: CrateDir( - // AbsolutePath( - // ".../wTools/module/move/wca", - // ), - // ), - // old_version: Version( - // Version { - // major: 0, - // minor: 12, - // patch: 0, - // }, - // ), - // new_version: Version( - // Version { - // major: 0, - // minor: 13, - // patch: 0, - // }, - // ), - // dependencies: [ - // CrateDir( - // AbsolutePath( - // ".../wTools", - // ), - // ), - // ], - // }, - // git_things: GitThingsPlan { - // git_root: AbsolutePath( - // ".../wTools", - // ), - // items: [ - // AbsolutePath( - // ".../wTools/Cargo.toml", - // ), - // AbsolutePath( - // ".../wTools/module/move/wca/Cargo.toml", - // ), - // ], - // message: "wca-v0.13.0", - // }, - // publish: CargoPublishPlan { - // crate_dir: CrateDir( - // AbsolutePath( - // ".../wTools/module/move/wca", - // ), - // ), - // base_temp_dir: Some( - // "temp", - // ), - // }, - // }, - // ] - let mega_plan = mega_plan.perform( true ); +// [module\move\willbe\tests\inc\package.rs:21:3] &mega_plan.plans = [ +// PackagePublishInstruction { +// pack: PackOptions { +// path: ".../wTools/module/move/wca", +// temp_path: Some( +// "temp", +// ), +// dry: true, +// }, +// version_bump: BumpOptions { +// crate_dir: CrateDir( +// AbsolutePath( +// ".../wTools/module/move/wca", +// ), +// ), +// old_version: Version( +// Version { +// major: 0, +// minor: 14, +// patch: 0, +// }, +// ), +// new_version: Version( +// Version { +// major: 0, +// minor: 15, +// patch: 0, +// }, +// ), +// dependencies: [ +// CrateDir( +// AbsolutePath( +// ".../wTools", +// ), +// ), +// ], +// dry: true, +// }, +// git_things: GitThingsOptions { +// git_root: AbsolutePath( +// ".../wTools", +// ), +// items: [ +// AbsolutePath( +// ".../wTools/Cargo.toml", +// ), +// AbsolutePath( +// ".../wTools/module/move/wca/Cargo.toml", +// ), +// ], +// message: "wca-v0.15.0", +// dry: true, +// }, +// publish: PublishOptions { +// path: ".../wTools/module/move/wca", +// temp_path: Some( +// "temp", +// ), +// dry: true, +// }, +// dry: true, +// }, +// ] + let mega_plan = perform_packages_publish( mega_plan ); dbg!( mega_plan ); - // [module/move/willbe/tests/inc/package.rs:21:3] mega_plan = Ok( - // [ - // PublishReport { - // get_info: Some( - // CmdReport { - // command: "cargo package --target-dir temp", - // path: ".../wTools/module/move/wca", - // out: "", - // err: "", - // }, - // ), - // publish_required: true, - // bump: Some( - // ExtendedBumpReport { - // base: BumpReport { - // name: Some( - // "wca", - // ), - // old_version: Some( - // "0.12.0", - // ), - // new_version: Some( - // "0.13.0", - // ), - // }, - // changed_files: [ - // AbsolutePath( - // ".../wTools/module/move/wca/Cargo.toml", - // ), - // AbsolutePath( - // ".../wTools/Cargo.toml", - // ), - // ], - // }, - // ), - // add: Some( - // CmdReport { - // command: "git add Cargo.toml module/move/wca/Cargo.toml", - // path: ".../wTools", - // out: "", - // err: "", - // }, - // ), - // commit: Some( - // CmdReport { - // command: "git commit -m wca-v0.13.0", - // path: ".../wTools", - // out: "", - // err: "", - // }, - // ), - // push: Some( - // CmdReport { - // command: "git push", - // path: ".../wTools", - // out: "", - // err: "", - // }, - // ), - // publish: Some( - // CmdReport { - // command: "cargo publish --target-dir temp", - // path: ".../wTools/module/move/wca", - // out: "", - // err: "", - // }, - // ), - // }, - // ], - // ) +// [module\move\willbe\tests\inc\package.rs:89:3] mega_plan = Ok( +// [ +// PublishReport { +// get_info: Some( +// Report { +// command: "cargo package --target-dir temp", +// current_path: ".../wTools/module/move/wca", +// out: "", +// err: "", +// error: Ok( +// (), +// ), +// }, +// ), +// publish_required: true, +// bump: Some( +// ExtendedBumpReport { +// base: BumpReport { +// name: Some( +// "wca", +// ), +// old_version: Some( +// "0.14.0", +// ), +// new_version: Some( +// "0.15.0", +// ), +// }, +// changed_files: [ +// AbsolutePath( +// ".../wTools/module/move/wca/Cargo.toml", +// ), +// AbsolutePath( +// ".../wTools/Cargo.toml", +// ), +// ], +// }, +// ), +// add: Some( +// Report { +// command: "git add Cargo.toml module/move/wca/Cargo.toml", +// current_path: ".../wTools", +// out: "", +// err: "", +// error: Ok( +// (), +// ), +// }, +// ), +// commit: Some( +// Report { +// command: "git commit -m wca-v0.15.0", +// current_path: ".../wTools", +// out: "", +// err: "", +// error: Ok( +// (), +// ), +// }, +// ), +// push: Some( +// Report { +// command: "git push", +// current_path: ".../wTools", +// out: "", +// err: "", +// error: Ok( +// (), +// ), +// }, +// ), +// publish: Some( +// Report { +// command: "cargo publish --target-dir temp", +// current_path: ".../wTools/module/move/wca", +// out: "", +// err: "", +// error: Ok( +// (), +// ), +// }, +// ), +// }, +// ], +// ) panic!() } From 10e92bdabb1d940da953f32ea469e710ddb06622 Mon Sep 17 00:00:00 2001 From: Barsik Date: Thu, 21 Mar 2024 12:26:41 +0200 Subject: [PATCH 07/20] Change of solved `qqq` -> `aaa` --- module/move/willbe/src/entity/manifest.rs | 8 ++++---- module/move/willbe/src/entity/package.rs | 6 +++--- module/move/willbe/src/tool/graph.rs | 2 +- module/move/willbe/tests/inc/entity/publish_need.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/module/move/willbe/src/entity/manifest.rs b/module/move/willbe/src/entity/manifest.rs index 72ef5df144..619a5bab8c 100644 --- a/module/move/willbe/src/entity/manifest.rs +++ b/module/move/willbe/src/entity/manifest.rs @@ -39,7 +39,7 @@ pub( crate ) mod private impl TryFrom< AbsolutePath > for CrateDir { - // qqq : make better errors + // aaa : make better errors // aaa : use `CrateDirError` for it type Error = CrateDirError; @@ -100,7 +100,7 @@ pub( crate ) mod private impl TryFrom< AbsolutePath > for Manifest { - // qqq : make better errors + // aaa : make better errors // aaa : return `ManifestError` type Error = ManifestError; @@ -159,7 +159,7 @@ pub( crate ) mod private Ok( () ) } - // qqq : for Bohdan : don't abuse anyhow + // aaa : for Bohdan : don't abuse anyhow // aaa : return `io` error /// Store manifest. pub fn store( &self ) -> io::Result< () > @@ -200,7 +200,7 @@ pub( crate ) mod private } /// Create and load manifest by specified path - // qqq : for Bohdan : use newtype, add proper errors handing + // aaa : for Bohdan : use newtype, add proper errors handing // aaa : return `ManifestError` pub fn open( path : AbsolutePath ) -> Result< Manifest, ManifestError > { diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index acd5bc478d..e0331c0a91 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -71,7 +71,7 @@ mod private impl TryFrom< AbsolutePath > for Package { - // qqq : make better errors + // aaa : make better errors // aaa : return `PackageError` instead of `anohow` message type Error = PackageError; @@ -89,7 +89,7 @@ mod private impl TryFrom< Manifest > for Package { - // qqq : make better errors + // aaa : make better errors // aaa : return `PackageError` instead of `anohow` message type Error = PackageError; @@ -903,7 +903,7 @@ mod private .map( | p | p.join( format!( "package/{0}-{1}.crate", name, version ) ) ) .unwrap_or( packed_crate::local_path( &name, &version, package.crate_dir() ).map_err( | _ | PackageError::LocalPath )? ); - // qqq : for Bohdan : bad, properly handle errors + // aaa : for Bohdan : bad, properly handle errors // aaa : return result instead of panic let local_package = CrateArchive::read( local_package_path ).map_err( | _ | PackageError::ReadArchive )?; let remote_package = match CrateArchive::download_crates_io( name, version ) diff --git a/module/move/willbe/src/tool/graph.rs b/module/move/willbe/src/tool/graph.rs index 5c74ce9eb0..4e13a84fbc 100644 --- a/module/move/willbe/src/tool/graph.rs +++ b/module/move/willbe/src/tool/graph.rs @@ -97,7 +97,7 @@ pub( crate ) mod private .collect::< Vec< _ > >() ), Err( index ) => Err( GraphError::Cycle( ( *graph.index( index.node_id() ) ).clone() ) ), - // qqq : for Bohdan : bad, make proper error handling + // aaa : for Bohdan : bad, make proper error handling // aaa : now returns `GraphError` } } diff --git a/module/move/willbe/tests/inc/entity/publish_need.rs b/module/move/willbe/tests/inc/entity/publish_need.rs index fa0829b24c..59f4a97828 100644 --- a/module/move/willbe/tests/inc/entity/publish_need.rs +++ b/module/move/willbe/tests/inc/entity/publish_need.rs @@ -38,7 +38,7 @@ fn package< P : AsRef< Path > >( path : P ) -> Package fn no_changes() { // Arrange - // qqq : for Bohdan : make helper function returning package_path. reuse it for all relevant tests + // aaa : for Bohdan : make helper function returning package_path. reuse it for all relevant tests // aaa : use `package_path` function let package_path = package_path( "c" ); From 15dc59391ee8c23465c12e0ea042cd6af803cac8 Mon Sep 17 00:00:00 2001 From: Barsik Date: Thu, 21 Mar 2024 23:03:24 +0200 Subject: [PATCH 08/20] Refactor code to improve package publishing process Refactored the code base to simplify and improve the efficiency of the package publishing process. Notably, changed the `ExtendedBumpReport` structure for better version reporting in the publishing process, and enhanced the `PerformPackagesPublish` method with a new `PublishPlan` structure for managing multiple package publications. Additionally, updated some related function implementations and test cases to align with the changes. --- module/move/willbe/src/action/publish.rs | 67 +++-- module/move/willbe/src/entity/package.rs | 336 ++++++++++++----------- module/move/willbe/src/entity/version.rs | 25 +- module/move/willbe/tests/inc/package.rs | 326 +++++++++++----------- 4 files changed, 397 insertions(+), 357 deletions(-) diff --git a/module/move/willbe/src/action/publish.rs b/module/move/willbe/src/action/publish.rs index 9eb9162858..1f0b6f2acc 100644 --- a/module/move/willbe/src/action/publish.rs +++ b/module/move/willbe/src/action/publish.rs @@ -38,7 +38,6 @@ mod private .packages .iter() .filter_map( |( _, r )| r.bump.as_ref() ) - .map( | b | &b.base ) .filter_map( | b | b.name.as_ref().and_then( | name | b.old_version.as_ref().and_then( | old | b.new_version.as_ref().map( | new | ( name, ( old, new ) ) ) ) ) ) .collect::< HashMap< _, _ > >(); for wanted in &self.wanted_to_publish @@ -77,7 +76,7 @@ mod private { if let Some( bump ) = &package.bump { - match ( &bump.base.name, &bump.base.old_version, &bump.base.new_version ) + match ( &bump.name, &bump.old_version, &bump.new_version ) { ( Some( name ), Some( old ), Some( new ) ) => writeln!( f, "[{idx}] {name} ({old} -> {new})" )?, _ => {} @@ -85,7 +84,7 @@ mod private } } - write!( f, "\nActions :\n" )?; + writeln!( f, "\nActions :" )?; for ( path, report ) in &self.packages { let report = report.to_string().replace("\n", "\n "); @@ -98,7 +97,7 @@ mod private { path.as_ref() }; - f.write_fmt( format_args!( "Publishing crate by `{}` path\n {report}\n", path.display() ) )?; + writeln!( f, "Publishing crate by `{}` path\n {report}", path.display() )?; } Ok( () ) @@ -135,14 +134,12 @@ mod private Workspace::with_crate_dir( dir ).err_with( || report.clone() )? }; - report.workspace_root_dir = Some - ( - metadata - .workspace_root() - .err_with( || report.clone() )? - .try_into() - .err_with( || report.clone() )? - ); + let workspace_root_dir : AbsolutePath = metadata + .workspace_root() + .err_with( || report.clone() )? + .try_into() + .err_with( || report.clone() )?; + report.workspace_root_dir = Some( workspace_root_dir.clone() ); let packages = metadata.load().err_with( || report.clone() )?.packages().err_with( || report.clone() )?; let packages_to_publish : Vec< _ > = packages .iter() @@ -184,27 +181,37 @@ mod private let subgraph = graph::remove_not_required_to_publish( &package_map, &tmp, &packages_to_publish, dir.clone() ); let subgraph = subgraph.map( | _, n | n, | _, e | e ); - let queue = graph::toposort( subgraph ).unwrap().into_iter().map( | n | package_map.get( &n ).unwrap() ).collect::< Vec< _ > >(); + let queue = graph::toposort( subgraph ).unwrap().into_iter().map( | n | package_map.get( &n ).unwrap() ).cloned().collect::< Vec< _ > >(); - for package in queue + let plan = package::PublishPlan::former() + .workspace_dir( CrateDir::try_from( workspace_root_dir ).unwrap() ) + .option_base_temp_dir( dir.clone() ) + .packages( queue ) + .form(); + for package_report in package::perform_packages_publish( plan ).err_with( || report.clone() )? { - let args = package::PublishSingleOptions::former() - .package( package ) - .force( true ) - .option_base_temp_dir( &dir ) - .dry( dry ) - .form(); - let current_report = package::publish_single( args ) - .map_err - ( - | ( current_report, e ) | - { - report.packages.push(( package.crate_dir().absolute_path(), current_report.clone() )); - ( report.clone(), e.context( "Publish list of packages" ) ) - } - )?; - report.packages.push(( package.crate_dir().absolute_path(), current_report )); + let path : &std::path::Path = package_report.get_info.as_ref().unwrap().current_path.as_ref(); + report.packages.push(( AbsolutePath::try_from( path ).unwrap(), package_report )); } + // for package in queue + // { + // let args = package::PublishSingleOptions::former() + // .package( package ) + // .force( true ) + // .option_base_temp_dir( &dir ) + // .dry( dry ) + // .form(); + // let current_report = package::publish_single( args ) + // .map_err + // ( + // | ( current_report, e ) | + // { + // report.packages.push(( package.crate_dir().absolute_path(), current_report.clone() )); + // ( report.clone(), e.context( "Publish list of packages" ) ) + // } + // )?; + // report.packages.push(( package.crate_dir().absolute_path(), current_report )); + // } if temp { diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index 834e90c3cf..c8b662de5e 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -19,7 +19,6 @@ mod private use workspace::Workspace; use _path::AbsolutePath; - use version::BumpReport; use wtools:: { @@ -297,24 +296,24 @@ mod private pub dry : bool, } - fn perform_git_operations(args : GitThingsOptions ) -> Result< ExtendedGitReport > + fn perform_git_operations( o : GitThingsOptions ) -> Result< ExtendedGitReport > { let mut report = ExtendedGitReport::default(); - if args.items.is_empty() { return Ok( report ); } - let items = args + if o.items.is_empty() { return Ok( report ); } + let items = o .items .iter() .map ( - | item | item.as_ref().strip_prefix( args.git_root.as_ref() ).map( Path::to_string_lossy ) - .with_context( || format!( "git_root: {}, item: {}", args.git_root.as_ref().display(), item.as_ref().display() ) ) + | item | item.as_ref().strip_prefix( o.git_root.as_ref() ).map( Path::to_string_lossy ) + .with_context( || format!("git_root: {}, item: {}", o.git_root.as_ref().display(), item.as_ref().display() ) ) ) .collect::< Result< Vec< _ > > >()?; - let res = git::add( &args.git_root, &items, args.dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let res = git::add( &o.git_root, &items, o.dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; report.add = Some( res ); - let res = git::commit( &args.git_root, &args.message, args.dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let res = git::commit( &o.git_root, &o.message, o.dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; report.commit = Some( res ); - let res = git::push( &args.git_root, args.dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let res = git::push( &o.git_root, o.dry ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; report.push = Some( res ); Ok( report ) @@ -331,11 +330,12 @@ mod private pub dry : bool, } + /// Represents a planner for publishing a single package. #[ derive( Debug, Former ) ] #[ perform( fn build() -> PackagePublishInstruction ) ] pub struct PublishSinglePackagePlanner { - workspace : Workspace, + workspace_dir : CrateDir, package : Package, base_temp_dir : Option< PathBuf >, } @@ -345,7 +345,7 @@ mod private fn build( self ) -> PackagePublishInstruction { let crate_dir = self.package.crate_dir(); - let workspace_root : AbsolutePath = self.workspace.workspace_root().unwrap().try_into().unwrap(); + let workspace_root : AbsolutePath = self.workspace_dir.absolute_path(); let pack = cargo::PackOptions { path : crate_dir.as_ref().into(), @@ -398,7 +398,7 @@ mod private /// # Returns /// /// * `Result` - The result of the publishing operation, including information about the publish, version bump, and git operations. - pub fn perform_package_publish( args : PackagePublishInstruction ) -> Result< PublishReport > + pub fn perform_package_publish( instruction : PackagePublishInstruction ) -> Result< PublishReport > { let mut report = PublishReport::default(); let PackagePublishInstruction @@ -408,7 +408,7 @@ mod private mut git_things, mut publish, dry, - } = args; + } = instruction; pack.dry = dry; version_bump.dry = dry; git_things.dry = dry; @@ -427,25 +427,49 @@ mod private Ok( report ) } + /// `PublishPlan` manages the overall publication process for multiple packages. + /// It organizes the necessary details required for publishing each individual package. + /// This includes the workspace root directory, any temporary directories used during the process, + /// and the set of specific instructions for publishing each package. #[ derive( Debug, Former ) ] pub struct PublishPlan { - pub workspace : Workspace, + /// `workspace_dir` - This is the root directory of your workspace, containing all the Rust crates + /// that make up your package. It is used to locate the packages within your workspace that are meant + /// to be published. The value here is represented by `CrateDir` which indicates the directory of the crate. + pub workspace_dir : CrateDir, + + /// `base_temp_dir` - This is used for any temporary operations during the publication process, like + /// building the package or any other processes that might require the storage of transient data. It's + /// optional as not all operations will require temporary storage. The type used is `PathBuf` which allows + /// manipulation of the filesystem paths. pub base_temp_dir : Option< PathBuf >, + + /// `plans` - This is a vector containing the instructions for publishing each package. Each item + /// in the `plans` vector indicates a `PackagePublishInstruction` set for a single package. It outlines + /// how to build and where to publish the package amongst other instructions. The `#[setter( false )]` + /// attribute indicates that there is no setter method for the `plans` variable and it can only be modified + /// within the struct. #[ setter( false ) ] pub plans : Vec< PackagePublishInstruction >, } impl PublishPlanFormer { + pub fn option_base_temp_dir( mut self, path : Option< PathBuf > ) -> Self + { + self.storage.base_temp_dir = path; + self + } + pub fn package< IntoPackage >( mut self, package : IntoPackage ) -> Self where IntoPackage : Into< Package >, { let mut plan = PublishSinglePackagePlanner::former(); - if let Some( workspace ) = &self.storage.workspace + if let Some( workspace ) = &self.storage.workspace_dir { - plan = plan.workspace( workspace.clone() ); + plan = plan.workspace_dir( workspace.clone() ); } if let Some( base_temp_dir ) = &self.storage.base_temp_dir { @@ -572,144 +596,144 @@ mod private } } - /// Option for publish single - #[ derive( Debug, Former ) ] - pub struct PublishSingleOptions< 'a > - { - package : &'a Package, - force : bool, - base_temp_dir : &'a Option< PathBuf >, - dry : bool, - } - - impl < 'a >PublishSingleOptionsFormer< 'a > - { - pub fn option_base_temp_dir( mut self, value : impl Into< &'a Option< PathBuf > > ) -> Self - { - self.storage.base_temp_dir = Some( value.into() ); - self - } - } - - /// Publishes a single package without publishing its dependencies. - /// - /// This function is designed to publish a single package. It does not publish any of the package's dependencies. - /// - /// Args : - /// - /// - package - a package that will be published - /// - dry - a flag that indicates whether to apply the changes or not - /// - true - do not publish, but only show what steps should be taken - /// - false - publishes the package - /// - /// Returns : - /// Returns a result containing a report indicating the result of the operation. - pub fn publish_single< 'a >( args : PublishSingleOptions< 'a > ) -> Result< PublishReport, ( PublishReport, wError ) > - { - let mut report = PublishReport::default(); - if args.package.local_is().map_err( | err | ( report.clone(), format_err!( err ) ) )? - { - return Ok( report ); - } - - let package_dir = &args.package.crate_dir(); - let temp_dir = args.base_temp_dir.as_ref().map - ( - | p | - { - let path = p.join( package_dir.as_ref().file_name().unwrap() ); - std::fs::create_dir_all( &path ).unwrap(); - path - } - ); - - let pack_args = cargo::PackOptions::former() - .path( package_dir.absolute_path().as_ref().to_path_buf() ) - .option_temp_path( temp_dir.clone() ) - .dry( args.dry ) - .form(); - let output = cargo::pack( pack_args ).context( "Take information about package" ).map_err( | e | ( report.clone(), e ) )?; - if output.err.contains( "not yet committed") - { - return Err(( report, format_err!( "Some changes wasn't committed. Please, commit or stash that changes and try again." ) )); - } - report.get_info = Some( output ); - - if args.force || publish_need( &args.package, temp_dir.clone() ).map_err( | err | ( report.clone(), format_err!( err ) ) )? - { - report.publish_required = true; - - let mut files_changed_for_bump = vec![]; - let mut manifest = args.package.manifest().map_err( | err | ( report.clone(), format_err!( err ) ) )?; - // bump a version in the package manifest - let bump_report = version::bump( &mut manifest, args.dry ).context( "Try to bump package version" ).map_err( | e | ( report.clone(), e ) )?; - files_changed_for_bump.push( args.package.manifest_path() ); - let new_version = bump_report.new_version.clone().unwrap(); - - let package_name = args.package.name().map_err( | err | ( report.clone(), format_err!( err ) ) )?; - - // bump the package version in dependents (so far, only workspace) - let workspace_manifest_dir : AbsolutePath = Workspace::with_crate_dir( args.package.crate_dir() ).map_err( | err | ( report.clone(), err ) )?.workspace_root().map_err( | err | ( report.clone(), format_err!( err ) ) )?.try_into().unwrap(); - let workspace_manifest_path = workspace_manifest_dir.join( "Cargo.toml" ); - - // qqq : should be refactored - if !args.dry - { - let mut workspace_manifest = manifest::open( workspace_manifest_path.clone() ).map_err( | e | ( report.clone(), format_err!( e ) ) )?; - let workspace_manifest_data = workspace_manifest.manifest_data.as_mut().ok_or_else( || ( report.clone(), format_err!( PackageError::Manifest( ManifestError::EmptyManifestData ) ) ) )?; - workspace_manifest_data - .get_mut( "workspace" ) - .and_then( | workspace | workspace.get_mut( "dependencies" ) ) - .and_then( | dependencies | dependencies.get_mut( &package_name ) ) - .map - ( - | dependency | - { - if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) - { - if previous_version.starts_with('~') - { - dependency[ "version" ] = value( format!( "~{new_version}" ) ); - } - else - { - dependency[ "version" ] = value( new_version.clone() ); - } - } - } - ) - .unwrap(); - workspace_manifest.store().map_err( | err | ( report.clone(), err.into() ) )?; - } - - files_changed_for_bump.push( workspace_manifest_path ); - let files_changed_for_bump : Vec< _ > = files_changed_for_bump.into_iter().unique().collect(); - let objects_to_add : Vec< _ > = files_changed_for_bump.iter().map( | f | f.as_ref().strip_prefix( &workspace_manifest_dir ).unwrap().to_string_lossy() ).collect(); - - report.bump = Some( version::ExtendedBumpReport { base : bump_report, changed_files : files_changed_for_bump.clone() } ); - - let commit_message = format!( "{package_name}-v{new_version}" ); - let res = git::add( workspace_manifest_dir, objects_to_add, args.dry ).map_err( | e | ( report.clone(), e ) )?; - report.add = Some( res ); - let res = git::commit( package_dir, commit_message, args.dry ).map_err( | e | ( report.clone(), e ) )?; - report.commit = Some( res ); - let res = git::push( package_dir, args.dry ).map_err( | e | ( report.clone(), e ) )?; - report.push = Some( res ); - - let res = cargo::publish - ( - cargo::PublishOptions::former() - .path( package_dir.absolute_path().as_ref().to_path_buf() ) - .option_temp_path( temp_dir ) - .dry( args.dry ) - .form() - ) - .map_err( | e | ( report.clone(), e ) )?; - report.publish = Some( res ); - } - - Ok( report ) - } + // /// Option for publish single + // #[ derive( Debug, Former ) ] + // pub struct PublishSingleOptions< 'a > + // { + // package : &'a Package, + // force : bool, + // base_temp_dir : &'a Option< PathBuf >, + // dry : bool, + // } + // + // impl < 'a >PublishSingleOptionsFormer< 'a > + // { + // pub fn option_base_temp_dir( mut self, value : impl Into< &'a Option< PathBuf > > ) -> Self + // { + // self.storage.base_temp_dir = Some( value.into() ); + // self + // } + // } + // + // /// Publishes a single package without publishing its dependencies. + // /// + // /// This function is designed to publish a single package. It does not publish any of the package's dependencies. + // /// + // /// Args : + // /// + // /// - package - a package that will be published + // /// - dry - a flag that indicates whether to apply the changes or not + // /// - true - do not publish, but only show what steps should be taken + // /// - false - publishes the package + // /// + // /// Returns : + // /// Returns a result containing a report indicating the result of the operation. + // pub fn publish_single( o : PublishSingleOptions< '_ > ) -> Result< PublishReport, ( PublishReport, wError ) > + // { + // let mut report = PublishReport::default(); + // if o.package.local_is().map_err( | err | ( report.clone(), format_err!( err ) ) )? + // { + // return Ok( report ); + // } + // + // let package_dir = &o.package.crate_dir(); + // let temp_dir = o.base_temp_dir.as_ref().map + // ( + // | p | + // { + // let path = p.join( package_dir.as_ref().file_name().unwrap() ); + // std::fs::create_dir_all( &path ).unwrap(); + // path + // } + // ); + // + // let pack_args = cargo::PackOptions::former() + // .path( package_dir.absolute_path().as_ref().to_path_buf() ) + // .option_temp_path( temp_dir.clone() ) + // .dry( o.dry ) + // .form(); + // let output = cargo::pack( pack_args ).context( "Take information about package" ).map_err( | e | ( report.clone(), e ) )?; + // if output.err.contains( "not yet committed") + // { + // return Err(( report, format_err!( "Some changes wasn't committed. Please, commit or stash that changes and try again." ) )); + // } + // report.get_info = Some( output ); + // + // if o.force || publish_need( &o.package, temp_dir.clone() ).map_err( | err | ( report.clone(), format_err!( err ) ) )? + // { + // report.publish_required = true; + // + // let mut files_changed_for_bump = vec![]; + // let mut manifest = o.package.manifest().map_err( | err | ( report.clone(), format_err!( err ) ) )?; + // // bump a version in the package manifest + // let bump_report = version::bump( &mut manifest, o.dry ).context( "Try to bump package version" ).map_err( | e | ( report.clone(), e ) )?; + // files_changed_for_bump.push( o.package.manifest_path() ); + // let new_version = bump_report.new_version.clone().unwrap(); + // + // let package_name = o.package.name().map_err( |err | ( report.clone(), format_err!( err ) ) )?; + // + // // bump the package version in dependents (so far, only workspace) + // let workspace_manifest_dir : AbsolutePath = Workspace::with_crate_dir( o.package.crate_dir() ).map_err( | err | ( report.clone(), err ) )?.workspace_root().map_err( | err | ( report.clone(), format_err!( err ) ) )?.try_into().unwrap(); + // let workspace_manifest_path = workspace_manifest_dir.join( "Cargo.toml" ); + // + // // qqq : should be refactored + // if !o.dry + // { + // let mut workspace_manifest = manifest::open( workspace_manifest_path.clone() ).map_err( | e | ( report.clone(), format_err!( e ) ) )?; + // let workspace_manifest_data = workspace_manifest.manifest_data.as_mut().ok_or_else( || ( report.clone(), format_err!( PackageError::Manifest( ManifestError::EmptyManifestData ) ) ) )?; + // workspace_manifest_data + // .get_mut( "workspace" ) + // .and_then( | workspace | workspace.get_mut( "dependencies" ) ) + // .and_then( | dependencies | dependencies.get_mut( &package_name ) ) + // .map + // ( + // | dependency | + // { + // if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) + // { + // if previous_version.starts_with('~') + // { + // dependency[ "version" ] = value( format!( "~{new_version}" ) ); + // } + // else + // { + // dependency[ "version" ] = value( new_version.clone() ); + // } + // } + // } + // ) + // .unwrap(); + // workspace_manifest.store().map_err( | err | ( report.clone(), err.into() ) )?; + // } + // + // files_changed_for_bump.push( workspace_manifest_path ); + // let files_changed_for_bump : Vec< _ > = files_changed_for_bump.into_iter().unique().collect(); + // let objects_to_add : Vec< _ > = files_changed_for_bump.iter().map( | f | f.as_ref().strip_prefix( &workspace_manifest_dir ).unwrap().to_string_lossy() ).collect(); + // + // report.bump = Some( version::ExtendedBumpReport { base : bump_report, changed_files : files_changed_for_bump.clone() } ); + // + // let commit_message = format!( "{package_name}-v{new_version}" ); + // let res = git::add( workspace_manifest_dir, objects_to_add, o.dry ).map_err( | e | ( report.clone(), e ) )?; + // report.add = Some( res ); + // let res = git::commit( package_dir, commit_message, o.dry ).map_err( | e | ( report.clone(), e ) )?; + // report.commit = Some( res ); + // let res = git::push( package_dir, o.dry ).map_err( | e | ( report.clone(), e ) )?; + // report.push = Some( res ); + // + // let res = cargo::publish + // ( + // cargo::PublishOptions::former() + // .path( package_dir.absolute_path().as_ref().to_path_buf() ) + // .option_temp_path( temp_dir ) + // .dry( o.dry ) + // .form() + // ) + // .map_err( | e | ( report.clone(), e ) )?; + // report.publish = Some( res ); + // } + // + // Ok( report ) + // } /// Sorting variants for dependencies. #[ derive( Debug, Copy, Clone ) ] @@ -949,8 +973,8 @@ crate::mod_interface! protected use perform_packages_publish; protected use PublishReport; - protected use publish_single; - protected use PublishSingleOptions; + // protected use publish_single; + // protected use PublishSingleOptions; protected use Package; protected use PackageError; diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index cb08ce7a8e..27eadf371c 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -188,8 +188,12 @@ mod private #[ derive( Debug, Default, Clone ) ] pub struct ExtendedBumpReport { - /// Report base. - pub base : BumpReport, + /// Pacakge name. + pub name : Option< String >, + /// Package old version. + pub old_version : Option< String >, + /// Package new version. + pub new_version : Option< String >, /// Files that should(already) changed for bump. pub changed_files : Vec< AbsolutePath > } @@ -198,15 +202,20 @@ mod private { fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result { - let Self { base, changed_files } = self; + let Self { name, old_version, new_version, changed_files } = self; if self.changed_files.is_empty() { - f.write_str( "Files were not changed during bumping the version" )?; + write!( f, "Files were not changed during bumping the version" )?; return Ok( () ) } let files = changed_files.iter().map( | f | f.as_ref().display() ).join( ",\n " ); - f.write_fmt( format_args!( "{base}\n changed files :\n {files}\n" ) )?; + match ( name, old_version, new_version ) + { + ( Some( name ), Some( old_version ), Some( new_version ) ) + => writeln!( f, "`{name}` bumped from {old_version} to {new_version}\n changed files :\n {files}" ), + _ => writeln!( f, "Bump failed" ) + }?; Ok( () ) } @@ -229,15 +238,15 @@ mod private let package_path = args.crate_dir.absolute_path().join( "Cargo.toml" ); let package = Package::try_from( package_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; let name = package.name().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - report.base.name = Some( name.clone() ); + report.name = Some( name.clone() ); let package_version = package.version().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; let current_version = version::Version::try_from( package_version.as_str() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; if current_version > args.new_version { return Err( format_err!( "{report:?}\nThe current version of the package is higher than need to be set\n\tpackage: {name}\n\tcurrent_version: {current_version}\n\tnew_version: {}", args.new_version ) ); } - report.base.old_version = Some( args.old_version.to_string() ); - report.base.new_version = Some( args.new_version.to_string() ); + report.old_version = Some( args.old_version.to_string() ); + report.new_version = Some( args.new_version.to_string() ); let mut package_manifest = package.manifest().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; if !args.dry diff --git a/module/move/willbe/tests/inc/package.rs b/module/move/willbe/tests/inc/package.rs index 5e99c78fec..08b2f36f93 100644 --- a/module/move/willbe/tests/inc/package.rs +++ b/module/move/willbe/tests/inc/package.rs @@ -7,166 +7,166 @@ use the_module:: }; use willbe::package::perform_packages_publish; -#[ test ] -fn plan_publish_many_packages() -{ - let workspace = Workspace::from_current_path().unwrap(); - let package = workspace.package_find_by_manifest( /* AbsolutePath::try_from( "../wca/Cargo.toml" ).unwrap() */ ).unwrap().to_owned(); - let mega_plan = PublishPlan::former() - .workspace( workspace ) - .base_temp_dir( "temp" ) - .packages([ package ]) - .form(); - dbg!( &mega_plan.plans ); -// [module\move\willbe\tests\inc\package.rs:21:3] &mega_plan.plans = [ -// PackagePublishInstruction { -// pack: PackOptions { -// path: ".../wTools/module/move/wca", -// temp_path: Some( -// "temp", -// ), -// dry: true, -// }, -// version_bump: BumpOptions { -// crate_dir: CrateDir( -// AbsolutePath( -// ".../wTools/module/move/wca", -// ), -// ), -// old_version: Version( -// Version { -// major: 0, -// minor: 14, -// patch: 0, -// }, -// ), -// new_version: Version( -// Version { -// major: 0, -// minor: 15, -// patch: 0, -// }, -// ), -// dependencies: [ -// CrateDir( -// AbsolutePath( -// ".../wTools", -// ), -// ), -// ], -// dry: true, -// }, -// git_things: GitThingsOptions { -// git_root: AbsolutePath( -// ".../wTools", -// ), -// items: [ -// AbsolutePath( -// ".../wTools/Cargo.toml", -// ), -// AbsolutePath( -// ".../wTools/module/move/wca/Cargo.toml", -// ), -// ], -// message: "wca-v0.15.0", -// dry: true, -// }, -// publish: PublishOptions { -// path: ".../wTools/module/move/wca", -// temp_path: Some( -// "temp", -// ), -// dry: true, -// }, -// dry: true, -// }, -// ] - let mega_plan = perform_packages_publish( mega_plan ); - dbg!( mega_plan ); -// [module\move\willbe\tests\inc\package.rs:89:3] mega_plan = Ok( -// [ -// PublishReport { -// get_info: Some( -// Report { -// command: "cargo package --target-dir temp", -// current_path: ".../wTools/module/move/wca", -// out: "", -// err: "", -// error: Ok( -// (), -// ), -// }, -// ), -// publish_required: true, -// bump: Some( -// ExtendedBumpReport { -// base: BumpReport { -// name: Some( -// "wca", -// ), -// old_version: Some( -// "0.14.0", -// ), -// new_version: Some( -// "0.15.0", -// ), -// }, -// changed_files: [ -// AbsolutePath( -// ".../wTools/module/move/wca/Cargo.toml", -// ), -// AbsolutePath( -// ".../wTools/Cargo.toml", -// ), -// ], -// }, -// ), -// add: Some( -// Report { -// command: "git add Cargo.toml module/move/wca/Cargo.toml", -// current_path: ".../wTools", -// out: "", -// err: "", -// error: Ok( -// (), -// ), -// }, -// ), -// commit: Some( -// Report { -// command: "git commit -m wca-v0.15.0", -// current_path: ".../wTools", -// out: "", -// err: "", -// error: Ok( -// (), -// ), -// }, -// ), -// push: Some( -// Report { -// command: "git push", -// current_path: ".../wTools", -// out: "", -// err: "", -// error: Ok( -// (), -// ), -// }, -// ), -// publish: Some( -// Report { -// command: "cargo publish --target-dir temp", -// current_path: ".../wTools/module/move/wca", -// out: "", -// err: "", -// error: Ok( -// (), -// ), -// }, -// ), -// }, -// ], -// ) - panic!() -} +// #[ test ] +// fn plan_publish_many_packages() +// { +// let workspace = Workspace::from_current_path().unwrap(); +// let package = workspace.package_find_by_manifest( /* AbsolutePath::try_from( "../wca/Cargo.toml" ).unwrap() */ ).unwrap().to_owned(); +// let mega_plan = PublishPlan::former() +// .workspace( workspace ) +// .base_temp_dir( "temp" ) +// .packages([ package ]) +// .form(); +// dbg!( &mega_plan.plans ); +// // [module\move\willbe\tests\inc\package.rs:21:3] &mega_plan.plans = [ +// // PackagePublishInstruction { +// // pack: PackOptions { +// // path: ".../wTools/module/move/wca", +// // temp_path: Some( +// // "temp", +// // ), +// // dry: true, +// // }, +// // version_bump: BumpOptions { +// // crate_dir: CrateDir( +// // AbsolutePath( +// // ".../wTools/module/move/wca", +// // ), +// // ), +// // old_version: Version( +// // Version { +// // major: 0, +// // minor: 14, +// // patch: 0, +// // }, +// // ), +// // new_version: Version( +// // Version { +// // major: 0, +// // minor: 15, +// // patch: 0, +// // }, +// // ), +// // dependencies: [ +// // CrateDir( +// // AbsolutePath( +// // ".../wTools", +// // ), +// // ), +// // ], +// // dry: true, +// // }, +// // git_things: GitThingsOptions { +// // git_root: AbsolutePath( +// // ".../wTools", +// // ), +// // items: [ +// // AbsolutePath( +// // ".../wTools/Cargo.toml", +// // ), +// // AbsolutePath( +// // ".../wTools/module/move/wca/Cargo.toml", +// // ), +// // ], +// // message: "wca-v0.15.0", +// // dry: true, +// // }, +// // publish: PublishOptions { +// // path: ".../wTools/module/move/wca", +// // temp_path: Some( +// // "temp", +// // ), +// // dry: true, +// // }, +// // dry: true, +// // }, +// // ] +// let mega_plan = perform_packages_publish( mega_plan ); +// dbg!( mega_plan ); +// // [module\move\willbe\tests\inc\package.rs:89:3] mega_plan = Ok( +// // [ +// // PublishReport { +// // get_info: Some( +// // Report { +// // command: "cargo package --target-dir temp", +// // current_path: ".../wTools/module/move/wca", +// // out: "", +// // err: "", +// // error: Ok( +// // (), +// // ), +// // }, +// // ), +// // publish_required: true, +// // bump: Some( +// // ExtendedBumpReport { +// // base: BumpReport { +// // name: Some( +// // "wca", +// // ), +// // old_version: Some( +// // "0.14.0", +// // ), +// // new_version: Some( +// // "0.15.0", +// // ), +// // }, +// // changed_files: [ +// // AbsolutePath( +// // ".../wTools/module/move/wca/Cargo.toml", +// // ), +// // AbsolutePath( +// // ".../wTools/Cargo.toml", +// // ), +// // ], +// // }, +// // ), +// // add: Some( +// // Report { +// // command: "git add Cargo.toml module/move/wca/Cargo.toml", +// // current_path: ".../wTools", +// // out: "", +// // err: "", +// // error: Ok( +// // (), +// // ), +// // }, +// // ), +// // commit: Some( +// // Report { +// // command: "git commit -m wca-v0.15.0", +// // current_path: ".../wTools", +// // out: "", +// // err: "", +// // error: Ok( +// // (), +// // ), +// // }, +// // ), +// // push: Some( +// // Report { +// // command: "git push", +// // current_path: ".../wTools", +// // out: "", +// // err: "", +// // error: Ok( +// // (), +// // ), +// // }, +// // ), +// // publish: Some( +// // Report { +// // command: "cargo publish --target-dir temp", +// // current_path: ".../wTools/module/move/wca", +// // out: "", +// // err: "", +// // error: Ok( +// // (), +// // ), +// // }, +// // ), +// // }, +// // ], +// // ) +// panic!() +// } From d6aa3492d2655628007c072c92aad462674e2c22 Mon Sep 17 00:00:00 2001 From: Barsik Date: Fri, 22 Mar 2024 00:29:17 +0200 Subject: [PATCH 09/20] test_experimental_c-v0.3.0 --- module/test/c/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/test/c/Cargo.toml b/module/test/c/Cargo.toml index 584207f2d5..8b5d415955 100644 --- a/module/test/c/Cargo.toml +++ b/module/test/c/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_experimental_c" -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "MIT" description = """ From 4745052130e4d2766f36797a25581ea0dcd7299e Mon Sep 17 00:00:00 2001 From: Barsik Date: Fri, 22 Mar 2024 00:34:03 +0200 Subject: [PATCH 10/20] Upgrade library dependency and refactor publish operation The library dependency version in Cargo.toml is updated. The trait Clone is added to BumpOptions, GitThingsOptions, and PackagePublishInstruction structs for proper duplication. The publishing operation is refactored to streamline the process, enhance readability, and improve performance. A PublishPlan struct is introduced, providing a better structure for handling package publication details. The new implementation correctly handles and displays package version updates. --- Cargo.toml | 2 +- module/move/willbe/src/action/publish.rs | 78 +------ module/move/willbe/src/entity/package.rs | 256 +++++++++-------------- module/move/willbe/src/entity/version.rs | 2 +- 4 files changed, 113 insertions(+), 225 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3d6db907de..5718b052fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -469,7 +469,7 @@ path = "module/test/b" default-features = true [workspace.dependencies.test_experimental_c] -version = "~0.2.0" +version = "~0.3.0" path = "module/test/c" default-features = true diff --git a/module/move/willbe/src/action/publish.rs b/module/move/willbe/src/action/publish.rs index 1f0b6f2acc..94a6e940c6 100644 --- a/module/move/willbe/src/action/publish.rs +++ b/module/move/willbe/src/action/publish.rs @@ -20,6 +20,7 @@ mod private pub workspace_root_dir : Option< AbsolutePath >, /// Represents a collection of packages that are roots of the trees. pub wanted_to_publish : Vec< CrateDir >, + pub plan : Option< package::PublishPlan >, /// Represents a collection of packages and their associated publishing reports. pub packages : Vec<( AbsolutePath, package::PublishReport )> } @@ -30,58 +31,16 @@ mod private { if self.packages.is_empty() { - f.write_fmt( format_args!( "Nothing to publish" ) )?; + write!( f, "Nothing to publish" )?; return Ok( () ); } - write!( f, "Tree(-s):\n" )?; - let name_bump_report = self - .packages - .iter() - .filter_map( |( _, r )| r.bump.as_ref() ) - .filter_map( | b | b.name.as_ref().and_then( | name | b.old_version.as_ref().and_then( | old | b.new_version.as_ref().map( | new | ( name, ( old, new ) ) ) ) ) ) - .collect::< HashMap< _, _ > >(); - for wanted in &self.wanted_to_publish + if let Some( plan ) = &self.plan { - let list = action::list - ( - action::list::ListOptions::former() - .path_to_manifest( wanted.clone() ) - .format( action::list::ListFormat::Tree ) - .dependency_sources([ action::list::DependencySource::Local ]) - .dependency_categories([ action::list::DependencyCategory::Primary ]) - .form() - ) - .map_err( |( _, _e )| std::fmt::Error )?; - let action::list::ListReport::Tree( list ) = list else { unreachable!() }; - - fn callback( name_bump_report : &HashMap< &String, ( &String, &String) >, mut r : action::list::ListNodeReport ) -> action::list::ListNodeReport - { - if let Some(( old, new )) = name_bump_report.get( &r.name ) - { - r.version = Some( format!( "({old} -> {new})" ) ); - } - r.normal_dependencies = r.normal_dependencies.into_iter().map( | r | callback( name_bump_report, r ) ).collect(); - r.dev_dependencies = r.dev_dependencies.into_iter().map( | r | callback( name_bump_report, r ) ).collect(); - r.build_dependencies = r.build_dependencies.into_iter().map( | r | callback( name_bump_report, r ) ).collect(); - - r - } - let list = list.into_iter().map( | r | callback( &name_bump_report, r ) ).collect(); + write!( f, "Tree{} :\n", if self.wanted_to_publish.len() > 1 { "s" } else { "" } )?; + plan.display_as_tree( f, &self.wanted_to_publish )?; - let list = action::list::ListReport::Tree( list ); - write!( f, "{}\n", list )?; - } - writeln!( f, "The following packages are pending for publication :" )?; - for ( idx, package ) in self.packages.iter().map( |( _, p )| p ).enumerate() - { - if let Some( bump ) = &package.bump - { - match ( &bump.name, &bump.old_version, &bump.new_version ) - { - ( Some( name ), Some( old ), Some( new ) ) => writeln!( f, "[{idx}] {name} ({old} -> {new})" )?, - _ => {} - } - } + writeln!( f, "The following packages are pending for publication :" )?; + plan.display_as_list( f )?; } writeln!( f, "\nActions :" )?; @@ -97,7 +56,7 @@ mod private { path.as_ref() }; - writeln!( f, "Publishing crate by `{}` path\n {report}", path.display() )?; + write!( f, "Publishing crate by `{}` path\n {report}", path.display() )?; } Ok( () ) @@ -186,32 +145,15 @@ mod private let plan = package::PublishPlan::former() .workspace_dir( CrateDir::try_from( workspace_root_dir ).unwrap() ) .option_base_temp_dir( dir.clone() ) + .dry( dry ) .packages( queue ) .form(); + report.plan = Some( plan.clone() ); for package_report in package::perform_packages_publish( plan ).err_with( || report.clone() )? { let path : &std::path::Path = package_report.get_info.as_ref().unwrap().current_path.as_ref(); report.packages.push(( AbsolutePath::try_from( path ).unwrap(), package_report )); } - // for package in queue - // { - // let args = package::PublishSingleOptions::former() - // .package( package ) - // .force( true ) - // .option_base_temp_dir( &dir ) - // .dry( dry ) - // .form(); - // let current_report = package::publish_single( args ) - // .map_err - // ( - // | ( current_report, e ) | - // { - // report.packages.push(( package.crate_dir().absolute_path(), current_report.clone() )); - // ( report.clone(), e.context( "Publish list of packages" ) ) - // } - // )?; - // report.packages.push(( package.crate_dir().absolute_path(), current_report )); - // } if temp { diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index c8b662de5e..608008f1ec 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -287,7 +287,7 @@ mod private pub push : Option< process::Report >, } - #[ derive( Debug ) ] + #[ derive( Debug, Clone ) ] pub struct GitThingsOptions { pub git_root : AbsolutePath, @@ -319,9 +319,10 @@ mod private Ok( report ) } - #[ derive( Debug ) ] + #[ derive( Debug, Clone ) ] pub struct PackagePublishInstruction { + pub package_name : String, pub pack : cargo::PackOptions, pub version_bump : version::BumpOptions, // qqq : rename @@ -338,6 +339,8 @@ mod private workspace_dir : CrateDir, package : Package, base_temp_dir : Option< PathBuf >, + #[ default( true ) ] + dry : bool, } impl PublishSinglePackagePlanner @@ -350,7 +353,7 @@ mod private { path : crate_dir.as_ref().into(), temp_path : self.base_temp_dir.clone(), - dry : true, + dry : self.dry, }; let old_version : version::Version = self.package.version().as_ref().unwrap().try_into().unwrap(); let new_version = old_version.clone().bump(); @@ -362,29 +365,30 @@ mod private old_version : old_version.clone(), new_version : new_version.clone(), dependencies : dependencies.clone(), - dry : true, + dry : self.dry, }; let git_things = GitThingsOptions { git_root : workspace_root, items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.absolute_path().join( "Cargo.toml" ) ).collect(), message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), - dry : true, + dry : self.dry, }; let publish = cargo::PublishOptions { path : crate_dir.as_ref().into(), temp_path : self.base_temp_dir.clone(), - dry : true, + dry : self.dry, }; PackagePublishInstruction { + package_name : self.package.name().unwrap(), pack, version_bump, git_things, publish, - dry : true, + dry : self.dry, } } } @@ -403,6 +407,7 @@ mod private let mut report = PublishReport::default(); let PackagePublishInstruction { + package_name: _, mut pack, mut version_bump, mut git_things, @@ -431,7 +436,7 @@ mod private /// It organizes the necessary details required for publishing each individual package. /// This includes the workspace root directory, any temporary directories used during the process, /// and the set of specific instructions for publishing each package. - #[ derive( Debug, Former ) ] + #[ derive( Debug, Former, Clone ) ] pub struct PublishPlan { /// `workspace_dir` - This is the root directory of your workspace, containing all the Rust crates @@ -445,6 +450,9 @@ mod private /// manipulation of the filesystem paths. pub base_temp_dir : Option< PathBuf >, + #[ default( true ) ] + pub dry : bool, + /// `plans` - This is a vector containing the instructions for publishing each package. Each item /// in the `plans` vector indicates a `PackagePublishInstruction` set for a single package. It outlines /// how to build and where to publish the package amongst other instructions. The `#[setter( false )]` @@ -454,6 +462,81 @@ mod private pub plans : Vec< PackagePublishInstruction >, } + impl PublishPlan + { + /// Displays a tree-like structure of crates and their dependencies. + /// + /// # Arguments + /// + /// * `f` - A mutable reference to a `Formatter` used for writing the output. + /// * `roots` - A slice of `CrateDir` representing the root crates to display. + /// + /// # Errors + /// + /// Returns a `std::fmt::Error` if there is an error writing to the formatter. + pub fn display_as_tree( &self, f : &mut Formatter< '_ >, roots : &[ CrateDir ] ) -> std::fmt::Result + { + let name_bump_report = self + .plans + .iter() + .map( | x | ( &x.package_name, ( x.version_bump.old_version.to_string(), x.version_bump.new_version.to_string() ) ) ) + .collect::< HashMap< _, _ > >(); + for wanted in roots + { + let list = action::list + ( + action::list::ListOptions::former() + .path_to_manifest( wanted.clone() ) + .format( action::list::ListFormat::Tree ) + .dependency_sources([ action::list::DependencySource::Local ]) + .dependency_categories([ action::list::DependencyCategory::Primary ]) + .form() + ) + .map_err( |( _, _e )| std::fmt::Error )?; + let action::list::ListReport::Tree( list ) = list else { unreachable!() }; + + fn callback( name_bump_report : &HashMap< &String, ( String, String ) >, mut r : action::list::ListNodeReport ) -> action::list::ListNodeReport + { + if let Some(( old, new )) = name_bump_report.get( &r.name ) + { + r.version = Some( format!( "({old} -> {new})" ) ); + } + r.normal_dependencies = r.normal_dependencies.into_iter().map( | r | callback( name_bump_report, r ) ).collect(); + r.dev_dependencies = r.dev_dependencies.into_iter().map( | r | callback( name_bump_report, r ) ).collect(); + r.build_dependencies = r.build_dependencies.into_iter().map( | r | callback( name_bump_report, r ) ).collect(); + + r + } + let list = list.into_iter().map( | r | callback( &name_bump_report, r ) ).collect(); + + let list = action::list::ListReport::Tree( list ); + writeln!( f, "{}", list )?; + } + + Ok( () ) + } + + /// Format and display the list of packages and their version bumps in a formatted way. + /// + /// # Arguments + /// + /// - `f`: A mutable reference to a `Formatter` where the output will be written to. + /// + /// # Errors + /// + /// Returns a `std::fmt::Error` if there is an error writing to the formatter. + pub fn display_as_list( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + { + for ( idx, package ) in self.plans.iter().enumerate() + { + let bump = &package.version_bump; + writeln!( f, "[{idx}] {} ({} -> {})", package.package_name, bump.old_version, bump.new_version )?; + } + + Ok( () ) + } + } + impl PublishPlanFormer { pub fn option_base_temp_dir( mut self, path : Option< PathBuf > ) -> Self @@ -475,6 +558,10 @@ mod private { plan = plan.base_temp_dir( base_temp_dir.clone() ); } + if let Some( dry ) = self.storage.dry + { + plan = plan.dry( dry ); + } let plan = plan .package( package ) .perform(); @@ -563,7 +650,7 @@ mod private return Ok( () ) } let info = get_info.as_ref().unwrap(); - f.write_fmt( format_args!( "{}", info ) )?; + write!( f, "{}", info )?; if !publish_required { @@ -573,168 +660,29 @@ mod private if let Some( bump ) = bump { - f.write_fmt( format_args!( "{}", bump ) )?; + writeln!( f, "{}", bump )?; } if let Some( add ) = add { - f.write_fmt( format_args!( "{add}" ) )?; + write!( f, "{add}" )?; } if let Some( commit ) = commit { - f.write_fmt( format_args!( "{commit}" ) )?; + write!( f, "{commit}" )?; } if let Some( push ) = push { - f.write_fmt( format_args!( "{push}" ) )?; + write!( f, "{push}" )?; } if let Some( publish ) = publish { - f.write_fmt( format_args!( "{publish}" ) )?; + write!( f, "{publish}" )?; } Ok( () ) } } - // /// Option for publish single - // #[ derive( Debug, Former ) ] - // pub struct PublishSingleOptions< 'a > - // { - // package : &'a Package, - // force : bool, - // base_temp_dir : &'a Option< PathBuf >, - // dry : bool, - // } - // - // impl < 'a >PublishSingleOptionsFormer< 'a > - // { - // pub fn option_base_temp_dir( mut self, value : impl Into< &'a Option< PathBuf > > ) -> Self - // { - // self.storage.base_temp_dir = Some( value.into() ); - // self - // } - // } - // - // /// Publishes a single package without publishing its dependencies. - // /// - // /// This function is designed to publish a single package. It does not publish any of the package's dependencies. - // /// - // /// Args : - // /// - // /// - package - a package that will be published - // /// - dry - a flag that indicates whether to apply the changes or not - // /// - true - do not publish, but only show what steps should be taken - // /// - false - publishes the package - // /// - // /// Returns : - // /// Returns a result containing a report indicating the result of the operation. - // pub fn publish_single( o : PublishSingleOptions< '_ > ) -> Result< PublishReport, ( PublishReport, wError ) > - // { - // let mut report = PublishReport::default(); - // if o.package.local_is().map_err( | err | ( report.clone(), format_err!( err ) ) )? - // { - // return Ok( report ); - // } - // - // let package_dir = &o.package.crate_dir(); - // let temp_dir = o.base_temp_dir.as_ref().map - // ( - // | p | - // { - // let path = p.join( package_dir.as_ref().file_name().unwrap() ); - // std::fs::create_dir_all( &path ).unwrap(); - // path - // } - // ); - // - // let pack_args = cargo::PackOptions::former() - // .path( package_dir.absolute_path().as_ref().to_path_buf() ) - // .option_temp_path( temp_dir.clone() ) - // .dry( o.dry ) - // .form(); - // let output = cargo::pack( pack_args ).context( "Take information about package" ).map_err( | e | ( report.clone(), e ) )?; - // if output.err.contains( "not yet committed") - // { - // return Err(( report, format_err!( "Some changes wasn't committed. Please, commit or stash that changes and try again." ) )); - // } - // report.get_info = Some( output ); - // - // if o.force || publish_need( &o.package, temp_dir.clone() ).map_err( | err | ( report.clone(), format_err!( err ) ) )? - // { - // report.publish_required = true; - // - // let mut files_changed_for_bump = vec![]; - // let mut manifest = o.package.manifest().map_err( | err | ( report.clone(), format_err!( err ) ) )?; - // // bump a version in the package manifest - // let bump_report = version::bump( &mut manifest, o.dry ).context( "Try to bump package version" ).map_err( | e | ( report.clone(), e ) )?; - // files_changed_for_bump.push( o.package.manifest_path() ); - // let new_version = bump_report.new_version.clone().unwrap(); - // - // let package_name = o.package.name().map_err( |err | ( report.clone(), format_err!( err ) ) )?; - // - // // bump the package version in dependents (so far, only workspace) - // let workspace_manifest_dir : AbsolutePath = Workspace::with_crate_dir( o.package.crate_dir() ).map_err( | err | ( report.clone(), err ) )?.workspace_root().map_err( | err | ( report.clone(), format_err!( err ) ) )?.try_into().unwrap(); - // let workspace_manifest_path = workspace_manifest_dir.join( "Cargo.toml" ); - // - // // qqq : should be refactored - // if !o.dry - // { - // let mut workspace_manifest = manifest::open( workspace_manifest_path.clone() ).map_err( | e | ( report.clone(), format_err!( e ) ) )?; - // let workspace_manifest_data = workspace_manifest.manifest_data.as_mut().ok_or_else( || ( report.clone(), format_err!( PackageError::Manifest( ManifestError::EmptyManifestData ) ) ) )?; - // workspace_manifest_data - // .get_mut( "workspace" ) - // .and_then( | workspace | workspace.get_mut( "dependencies" ) ) - // .and_then( | dependencies | dependencies.get_mut( &package_name ) ) - // .map - // ( - // | dependency | - // { - // if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) - // { - // if previous_version.starts_with('~') - // { - // dependency[ "version" ] = value( format!( "~{new_version}" ) ); - // } - // else - // { - // dependency[ "version" ] = value( new_version.clone() ); - // } - // } - // } - // ) - // .unwrap(); - // workspace_manifest.store().map_err( | err | ( report.clone(), err.into() ) )?; - // } - // - // files_changed_for_bump.push( workspace_manifest_path ); - // let files_changed_for_bump : Vec< _ > = files_changed_for_bump.into_iter().unique().collect(); - // let objects_to_add : Vec< _ > = files_changed_for_bump.iter().map( | f | f.as_ref().strip_prefix( &workspace_manifest_dir ).unwrap().to_string_lossy() ).collect(); - // - // report.bump = Some( version::ExtendedBumpReport { base : bump_report, changed_files : files_changed_for_bump.clone() } ); - // - // let commit_message = format!( "{package_name}-v{new_version}" ); - // let res = git::add( workspace_manifest_dir, objects_to_add, o.dry ).map_err( | e | ( report.clone(), e ) )?; - // report.add = Some( res ); - // let res = git::commit( package_dir, commit_message, o.dry ).map_err( | e | ( report.clone(), e ) )?; - // report.commit = Some( res ); - // let res = git::push( package_dir, o.dry ).map_err( | e | ( report.clone(), e ) )?; - // report.push = Some( res ); - // - // let res = cargo::publish - // ( - // cargo::PublishOptions::former() - // .path( package_dir.absolute_path().as_ref().to_path_buf() ) - // .option_temp_path( temp_dir ) - // .dry( o.dry ) - // .form() - // ) - // .map_err( | e | ( report.clone(), e ) )?; - // report.publish = Some( res ); - // } - // - // Ok( report ) - // } - /// Sorting variants for dependencies. #[ derive( Debug, Copy, Clone ) ] pub enum DependenciesSort @@ -973,8 +921,6 @@ crate::mod_interface! protected use perform_packages_publish; protected use PublishReport; - // protected use publish_single; - // protected use PublishSingleOptions; protected use Package; protected use PackageError; diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index 27eadf371c..73d45f02a7 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -174,7 +174,7 @@ mod private /// Options for version bumping. /// /// This struct is used to specify the options for version bumping operations. - #[ derive( Debug ) ] + #[ derive( Debug, Clone ) ] pub struct BumpOptions { pub crate_dir : CrateDir, From da8383647d1f7defe8af8f7049844ea739f715a0 Mon Sep 17 00:00:00 2001 From: Barsik Date: Fri, 22 Mar 2024 00:35:06 +0200 Subject: [PATCH 11/20] test_experimental_b-v0.3.0 --- Cargo.toml | 2 +- module/test/b/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5718b052fa..90287d22e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -464,7 +464,7 @@ path = "module/test/a" default-features = true [workspace.dependencies.test_experimental_b] -version = "~0.2.0" +version = "~0.3.0" path = "module/test/b" default-features = true diff --git a/module/test/b/Cargo.toml b/module/test/b/Cargo.toml index 6b3808e6d0..57d5bb293a 100644 --- a/module/test/b/Cargo.toml +++ b/module/test/b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_experimental_b" -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "MIT" description = """ From c80e55636bb6eec81de475174a34ce033dcbdaa6 Mon Sep 17 00:00:00 2001 From: Barsik Date: Fri, 22 Mar 2024 00:51:06 +0200 Subject: [PATCH 12/20] Refactor argument name in version_bump function Changed the name of the argument in the version_bump function from "args" to "o". This change affects multiple instances within the function where "args" was previously used. The rename aims to improve code readability and clarity. --- module/move/willbe/src/entity/version.rs | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index 73d45f02a7..b2427b3552 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -232,36 +232,36 @@ mod private /// /// Returns a result containing the extended bump report if successful. /// - pub fn version_bump( args : BumpOptions ) -> Result< ExtendedBumpReport > + pub fn version_bump( o : BumpOptions ) -> Result< ExtendedBumpReport > { let mut report = ExtendedBumpReport::default(); - let package_path = args.crate_dir.absolute_path().join( "Cargo.toml" ); + let package_path = o.crate_dir.absolute_path().join( "Cargo.toml" ); let package = Package::try_from( package_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; let name = package.name().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; report.name = Some( name.clone() ); let package_version = package.version().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; let current_version = version::Version::try_from( package_version.as_str() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - if current_version > args.new_version + if current_version > o.new_version { - return Err( format_err!( "{report:?}\nThe current version of the package is higher than need to be set\n\tpackage: {name}\n\tcurrent_version: {current_version}\n\tnew_version: {}", args.new_version ) ); + return Err( format_err!( "{report:?}\nThe current version of the package is higher than need to be set\n\tpackage: {name}\n\tcurrent_version: {current_version}\n\tnew_version: {}", o.new_version ) ); } - report.old_version = Some( args.old_version.to_string() ); - report.new_version = Some( args.new_version.to_string() ); + report.old_version = Some( o.old_version.to_string() ); + report.new_version = Some( o.new_version.to_string() ); let mut package_manifest = package.manifest().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - if !args.dry + if !o.dry { let data = package_manifest.manifest_data.as_mut().unwrap(); - data[ "package" ][ "version" ] = value( &args.new_version.to_string() ); + data[ "package" ][ "version" ] = value( &o.new_version.to_string() ); package_manifest.store()?; } report.changed_files = vec![ package_path ]; - let new_version = &args.new_version.to_string(); - for dep in &args.dependencies + let new_version = &o.new_version.to_string(); + for dep in &o.dependencies { let manifest_path = dep.absolute_path().join( "Cargo.toml" ); - let manifest = manifest::open( manifest_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; - let data = package_manifest.manifest_data.as_mut().unwrap(); + let mut manifest = manifest::open( manifest_path.clone() ).map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; + let data = manifest.manifest_data.as_mut().unwrap(); let item = if let Some( item ) = data.get_mut( "package" ) { item } else if let Some( item ) = data.get_mut( "workspace" ) { item } else { return Err( format_err!( "{report:?}\nThe manifest nor the package and nor the workspace" ) ); }; @@ -279,7 +279,7 @@ mod private } } } - if !args.dry { manifest.store().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; } + if !o.dry { manifest.store().map_err( | e | format_err!( "{report:?}\n{e:#?}" ) )?; } report.changed_files.push( manifest_path ); } From 3783fd2e99c898bc35d634b624df457134fdc3c6 Mon Sep 17 00:00:00 2001 From: Barsik Date: Fri, 22 Mar 2024 00:51:35 +0200 Subject: [PATCH 13/20] test_experimental_a-v0.4.0 --- Cargo.toml | 2 +- module/test/a/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90287d22e0..a820db7eb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -459,7 +459,7 @@ default-features = true ## test experimental [workspace.dependencies.test_experimental_a] -version = "~0.3.0" +version = "~0.4.0" path = "module/test/a" default-features = true diff --git a/module/test/a/Cargo.toml b/module/test/a/Cargo.toml index 81dfa753e8..4993eed4d5 100644 --- a/module/test/a/Cargo.toml +++ b/module/test/a/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_experimental_a" -version = "0.3.0" +version = "0.4.0" edition = "2021" license = "MIT" description = """ From 534694dc922ecc91872d37139bcfa61de3b5e228 Mon Sep 17 00:00:00 2001 From: Barsik Date: Fri, 22 Mar 2024 00:52:49 +0200 Subject: [PATCH 14/20] test_experimental_a-v0.5.0 --- Cargo.toml | 2 +- module/test/a/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a820db7eb2..f058e4f666 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -459,7 +459,7 @@ default-features = true ## test experimental [workspace.dependencies.test_experimental_a] -version = "~0.4.0" +version = "~0.5.0" path = "module/test/a" default-features = true diff --git a/module/test/a/Cargo.toml b/module/test/a/Cargo.toml index 4993eed4d5..19f5f8e546 100644 --- a/module/test/a/Cargo.toml +++ b/module/test/a/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_experimental_a" -version = "0.4.0" +version = "0.5.0" edition = "2021" license = "MIT" description = """ From 40037d35809c06956fafa07a47f18a24e0b73621 Mon Sep 17 00:00:00 2001 From: Barsik Date: Fri, 22 Mar 2024 00:58:07 +0200 Subject: [PATCH 15/20] Update struct comments and remove unused import Updated the comments for `BumpOptions` struct in `version.rs` to provide more detailed information about each field. Removed an unused import statement in `package.rs`. Also, an optional `dry` field has been added to the `DependentPackage` struct to check simulate run without making any actual changes. --- module/move/willbe/src/entity/package.rs | 6 ++++-- module/move/willbe/src/entity/version.rs | 21 ++++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index 27d05b8161..f64dfcb800 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -11,7 +11,6 @@ mod private use std::hash::Hash; use std::path::PathBuf; use cargo_metadata::{ Dependency, DependencyKind }; - use toml_edit::value; use process_tools::process; use manifest::{ Manifest, ManifestError }; @@ -28,7 +27,7 @@ mod private thiserror, Result, for_lib::Error, - for_app::{ format_err, Error as wError, Context }, + for_app::{ format_err, Context }, } }; use action::readme_health_table_renew::Stability; @@ -466,6 +465,9 @@ mod private /// manipulation of the filesystem paths. pub base_temp_dir : Option< PathBuf >, + /// `dry` - A boolean value indicating whether to do a dry run. If set to `true`, the application performs + /// a simulated run without making any actual changes. If set to `false`, the operations are actually executed. + /// This property is optional and defaults to `true`. #[ default( true ) ] pub dry : bool, diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index b2427b3552..50f851ce7d 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -171,16 +171,31 @@ mod private // qqq : we have to replace the implementation above with the implementation below, don't we? - /// Options for version bumping. - /// - /// This struct is used to specify the options for version bumping operations. + /// `BumpOptions` manages the details necessary for the version bump process for crates. + /// This includes the directory of the crate whose version is being bumped, the old and new version numbers, + /// and the set of dependencies of that crate. #[ derive( Debug, Clone ) ] pub struct BumpOptions { + /// `crate_dir` - The directory of the crate which you want to bump the version of. This value is + /// represented by `CrateDir` which indicates the directory of the crate. pub crate_dir : CrateDir, + + /// `old_version` - The version of the crate before the bump. It's represented by `Version` which + /// denotes the old version number of the crate. pub old_version : Version, + + /// `new_version` - The version number to assign to the crate after the bump. It's also represented + /// by `Version` which denotes the new version number of the crate. pub new_version : Version, + + /// `dependencies` - This is a vector containing the directories of all the dependencies of the crate. + /// Each item in the `dependencies` vector indicates a `CrateDir` directory of a single dependency. pub dependencies : Vec< CrateDir >, + + /// `dry` - A boolean indicating whether to do a "dry run". If set to `true`, a simulated run is performed + /// without making actual changes. If set to `false`, the operations are actually executed. This is + /// useful for validating the process of bumping up the version or for testing and debugging. pub dry : bool, } From d41cc5aa828bdb9eed90f6a549cad30b3e8aa69a Mon Sep 17 00:00:00 2001 From: SRetip Date: Fri, 22 Mar 2024 09:28:06 +0200 Subject: [PATCH 16/20] move display to wca & add sujecst --- module/move/wca/src/ca/grammar/types.rs | 38 ++++++++++++++++++++++ module/move/willbe/src/command/publish.rs | 4 ++- module/move/willbe/src/command/test.rs | 39 ----------------------- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/module/move/wca/src/ca/grammar/types.rs b/module/move/wca/src/ca/grammar/types.rs index 6a6ac6fe27..4021d34002 100644 --- a/module/move/wca/src/ca/grammar/types.rs +++ b/module/move/wca/src/ca/grammar/types.rs @@ -1,8 +1,14 @@ pub( crate ) mod private { use crate::*; + use std::fmt:: + { + Display, + Formatter + }; use wtools; use wtools::{ error::Result, err }; + use wtools::Itertools; /// Available types that can be converted to a `Value` /// @@ -87,6 +93,38 @@ pub( crate ) mod private List( Vec< Value > ), } + impl Display for Value + { + fn fmt( &self, f : &mut Formatter< '_ >) -> std::fmt::Result + { + match self + { + Value::String( s ) => + { + writeln!( f , "{s}" )?; + } + Value::Number( n ) => + { + writeln!( f, "{n}" )?; + } + Value::Path( p ) => + { + writeln!( f, "{}", p.display() )?; + } + Value::Bool( b ) => + { + writeln!( f, "{b}" )?; + } + Value::List( list ) => + { + let list = list.iter().map( | element | element.to_string() ).join( "," ); // qqq : don't hardcode ", " find way to get original separator + writeln!( f, "{list}" )?; + } + } + Ok( () ) + } + } + macro_rules! value_into_impl { ( $( $value_kind : path => $( $kind : ty => $cast : expr ),+ );+ ) => diff --git a/module/move/willbe/src/command/publish.rs b/module/move/willbe/src/command/publish.rs index ebc1f2b0e5..3aa1313649 100644 --- a/module/move/willbe/src/command/publish.rs +++ b/module/move/willbe/src/command/publish.rs @@ -13,6 +13,8 @@ mod private pub fn publish( args : Args, properties : Props ) -> Result< () > { + let args_line = format!( "{}", args.get_owned( 0 ).unwrap_or( "" ) ); + let prop_line = format!( "{}", properties.iter().map( | p | format!( "{}:{}", p.0, p.1.to_string() ) ).collect::< Vec< _ > >().join(" ") ); let patterns : Vec< _ > = args.get_owned( 0 ).unwrap_or_else( || vec![ "./".into() ] ); let dry : bool = properties @@ -31,7 +33,7 @@ mod private if dry && report.packages.iter().find( |( _, p )| p.publish_required ).is_some() { - println!( "To apply plan, call the command `will .publish dry:0`" ) + println!( "To apply plan, call the command `will .publish {} dry:0 {}`", args_line, prop_line ) // qqq : for Petro : for Bohdan : bad. should be exact command with exact parameters } diff --git a/module/move/willbe/src/command/test.rs b/module/move/willbe/src/command/test.rs index 9f418ed2a9..a3157fa988 100644 --- a/module/move/willbe/src/command/test.rs +++ b/module/move/willbe/src/command/test.rs @@ -9,7 +9,6 @@ mod private { Args, Props, - Value, }; use wtools::error::Result; use _path::AbsolutePath; @@ -17,46 +16,8 @@ mod private use former::Former; use channel::Channel; use error_tools::for_app::bail; - use iter_tools::Itertools; use optimization::Optimization; - - trait ToString - { - fn to_string( &self ) -> String; - } - - impl ToString for Value - { - fn to_string( &self ) -> String - { - match self - { - Value::String( s ) => - { - format!( "{s}" ) - } - Value::Number( n ) => - { - format!( "{n}" ) - } - Value::Path( p ) => - { - format!( "{}", p.display() ) - } - Value::Bool( b ) => - { - format!( "{b}" ) - } - Value::List( list ) => - { - let list = list.iter().map( | element | element.to_string() ).join( ", "); // qqq : don't hardcode ", " find way to get original separator - format!( "{list}" ) - } - } - } - } - #[ derive( Former, Debug ) ] struct TestsProperties { From 449f4a130210012340e5472eb45c2129b7423c07 Mon Sep 17 00:00:00 2001 From: SRetip Date: Fri, 22 Mar 2024 09:36:00 +0200 Subject: [PATCH 17/20] fix --- module/move/wca/src/ca/grammar/types.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/module/move/wca/src/ca/grammar/types.rs b/module/move/wca/src/ca/grammar/types.rs index 4021d34002..16126e0f03 100644 --- a/module/move/wca/src/ca/grammar/types.rs +++ b/module/move/wca/src/ca/grammar/types.rs @@ -101,24 +101,24 @@ pub( crate ) mod private { Value::String( s ) => { - writeln!( f , "{s}" )?; + write!( f , "{s}" )?; } Value::Number( n ) => { - writeln!( f, "{n}" )?; + write!( f, "{n}" )?; } Value::Path( p ) => { - writeln!( f, "{}", p.display() )?; + write!( f, "{}", p.display() )?; } Value::Bool( b ) => { - writeln!( f, "{b}" )?; + write!( f, "{b}" )?; } Value::List( list ) => { let list = list.iter().map( | element | element.to_string() ).join( "," ); // qqq : don't hardcode ", " find way to get original separator - writeln!( f, "{list}" )?; + write!( f, "{list}" )?; } } Ok( () ) From 54bc658337e53d8de603e8a617d89057c233ff5b Mon Sep 17 00:00:00 2001 From: Barsik-sus Date: Fri, 22 Mar 2024 10:30:48 +0200 Subject: [PATCH 18/20] exclude tests --- .../tests/inc/commands_aggregator/basic.rs | 45 ++++----- .../wca/tests/inc/commands_aggregator/mod.rs | 2 - module/move/wca/tests/inc/parser/command.rs | 98 +++++++++---------- 3 files changed, 72 insertions(+), 73 deletions(-) diff --git a/module/move/wca/tests/inc/commands_aggregator/basic.rs b/module/move/wca/tests/inc/commands_aggregator/basic.rs index 650470427b..2a47a7f4d4 100644 --- a/module/move/wca/tests/inc/commands_aggregator/basic.rs +++ b/module/move/wca/tests/inc/commands_aggregator/basic.rs @@ -243,27 +243,28 @@ tests_impls! a_id!( (), executor.command( dictionary, grammar_command ).unwrap() ); } - fn subject_with_spaces() - { - let query = "SELECT title, links, MIN( published ) FROM Frames"; - let ca = CommandsAggregator::former() - .grammar( - [ - wca::Command::former() - .hint( "hint" ) - .long_hint( "long_hint" ) - .phrase( "query.execute" ) - .subject( "SQL query", Type::String, false ) - .form(), - ]) - .executor( - [ - ( "query.execute".to_owned(), Routine::new( move |( args, _ )| { assert_eq!( query, args.get_owned::< &str >( 0 ).unwrap() ); Ok( () ) } ) ), - ]) - .build(); - - a_id!( (), ca.perform( vec![ ".query.execute".to_string(), query.into() ] ).unwrap() ); - } + // qqq : make the following test work + // fn subject_with_spaces() + // { + // let query = "SELECT title, links, MIN( published ) FROM Frames"; + // let ca = CommandsAggregator::former() + // .grammar( + // [ + // wca::Command::former() + // .hint( "hint" ) + // .long_hint( "long_hint" ) + // .phrase( "query.execute" ) + // .subject( "SQL query", Type::String, false ) + // .form(), + // ]) + // .executor( + // [ + // ( "query.execute".to_owned(), Routine::new( move |( args, _ )| { assert_eq!( query, args.get_owned::< &str >( 0 ).unwrap() ); Ok( () ) } ) ), + // ]) + // .build(); + + // a_id!( (), ca.perform( vec![ ".query.execute".to_string(), query.into() ] ).unwrap() ); + // } } // @@ -279,5 +280,5 @@ tests_index! string_subject_with_colon, no_prop_subject_with_colon, optional_prop_subject_with_colon, - subject_with_spaces, + // subject_with_spaces, } diff --git a/module/move/wca/tests/inc/commands_aggregator/mod.rs b/module/move/wca/tests/inc/commands_aggregator/mod.rs index c61ac51139..ca0cdc4b5a 100644 --- a/module/move/wca/tests/inc/commands_aggregator/mod.rs +++ b/module/move/wca/tests/inc/commands_aggregator/mod.rs @@ -5,8 +5,6 @@ use the_module:: Parser, CommandsAggregator, - Routine, - Type, HelpVariants, Type, Error, diff --git a/module/move/wca/tests/inc/parser/command.rs b/module/move/wca/tests/inc/parser/command.rs index 4656fa7d95..35929c07ce 100644 --- a/module/move/wca/tests/inc/parser/command.rs +++ b/module/move/wca/tests/inc/parser/command.rs @@ -147,54 +147,54 @@ tests_impls! } // qqq : the parser must be able to accept a list of arguments(std::env::args()) - fn with_spaces_in_value() - { - let parser = Parser::former().form(); - - a_id! - ( - ParsedCommand - { - name : "command".into(), - subjects : vec![ "value with spaces".into() ], - properties : HashMap::new(), - }, - parser.command( vec![ ".command".to_string(), "value with spaces".into() ] ).unwrap() - ); - - a_id! - ( - ParsedCommand - { - name : "command".into(), - subjects : vec![], - properties : HashMap::from_iter([ ( "prop".into(), "value with spaces".into() ) ]), - }, - parser.command( vec![ ".command".to_string(), "prop:value with spaces".into() ] ).unwrap() - ); - - a_id! - ( - ParsedCommand - { - name : "command".into(), - subjects : vec![], - properties : HashMap::from_iter([ ( "prop".into(), "value with spaces".into() ) ]), - }, - parser.command( vec![ ".command".to_string(), "prop:".into(), "value with spaces".into() ] ).unwrap() - ); - - a_id! - ( - ParsedCommand - { - name : "command".into(), - subjects : vec![], - properties : HashMap::from_iter([ ( "prop".into(), "value with spaces".into() ) ]), - }, - parser.command( vec![ ".command".to_string(), "prop".into(), ":".into(), "value with spaces".into() ] ).unwrap() - ); - } + // fn with_spaces_in_value() + // { + // let parser = Parser::former().form(); + + // a_id! + // ( + // ParsedCommand + // { + // name : "command".into(), + // subjects : vec![ "value with spaces".into() ], + // properties : HashMap::new(), + // }, + // parser.command( vec![ ".command".to_string(), "value with spaces".into() ] ).unwrap() + // ); + + // a_id! + // ( + // ParsedCommand + // { + // name : "command".into(), + // subjects : vec![], + // properties : HashMap::from_iter([ ( "prop".into(), "value with spaces".into() ) ]), + // }, + // parser.command( vec![ ".command".to_string(), "prop:value with spaces".into() ] ).unwrap() + // ); + + // a_id! + // ( + // ParsedCommand + // { + // name : "command".into(), + // subjects : vec![], + // properties : HashMap::from_iter([ ( "prop".into(), "value with spaces".into() ) ]), + // }, + // parser.command( vec![ ".command".to_string(), "prop:".into(), "value with spaces".into() ] ).unwrap() + // ); + + // a_id! + // ( + // ParsedCommand + // { + // name : "command".into(), + // subjects : vec![], + // properties : HashMap::from_iter([ ( "prop".into(), "value with spaces".into() ) ]), + // }, + // parser.command( vec![ ".command".to_string(), "prop".into(), ":".into(), "value with spaces".into() ] ).unwrap() + // ); + // } fn not_only_alphanumeric_symbols() { @@ -437,7 +437,7 @@ tests_index! { basic, with_spaces, - with_spaces_in_value, + // with_spaces_in_value, not_only_alphanumeric_symbols, same_command_and_prop_delimeter, path_in_subject, From 87df523709b1cccdec7f4a73e163079d0c49fb1d Mon Sep 17 00:00:00 2001 From: Barsik-sus Date: Fri, 22 Mar 2024 11:27:29 +0200 Subject: [PATCH 19/20] Save remote version of the package --- module/move/willbe/src/action/publish_diff.rs | 28 +++++++++++++- module/move/willbe/src/command/mod.rs | 5 +++ .../move/willbe/src/command/publish_diff.rs | 37 +++++++++++++++++-- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/module/move/willbe/src/action/publish_diff.rs b/module/move/willbe/src/action/publish_diff.rs index d8f648aba7..06835dce1d 100644 --- a/module/move/willbe/src/action/publish_diff.rs +++ b/module/move/willbe/src/action/publish_diff.rs @@ -10,11 +10,19 @@ mod private use wtools::error::for_app::Result; use diff::{ DiffReport, crate_diff }; + /// Options for `publish_diff` command + #[ derive( Debug, former::Former ) ] + pub struct PublishDiffOptions + { + path : PathBuf, + keep_archive : Option< PathBuf >, + } + /// Return the differences between a local and remote package versions. #[ cfg_attr( feature = "tracing", tracing::instrument ) ] - pub fn publish_diff( path : PathBuf ) -> Result< DiffReport > + pub fn publish_diff( o : PublishDiffOptions ) -> Result< DiffReport > { - let path = AbsolutePath::try_from( path )?; + let path = AbsolutePath::try_from( o.path )?; let dir = CrateDir::try_from( path )?; let package = package::Package::try_from( dir.clone() )?; @@ -25,6 +33,21 @@ mod private let l = CrateArchive::read( packed_crate::local_path( name, version, dir )? )?; let r = CrateArchive::download_crates_io( name, version ).unwrap(); + if let Some( out_path ) = o.keep_archive + { + _ = std::fs::create_dir_all( &out_path ); + for path in r.list() + { + let local_path = out_path.join( path ); + let folder = local_path.parent().unwrap(); + _ = std::fs::create_dir_all( folder ); + + let content = r.content_bytes( path ).unwrap(); + + std::fs::write( local_path, content )?; + } + } + Ok( crate_diff( &l, &r ) ) } } @@ -33,6 +56,7 @@ mod private crate::mod_interface! { + orphan use PublishDiffOptions; /// Publishes the difference between the local and published versions of a package. orphan use publish_diff; } diff --git a/module/move/willbe/src/command/mod.rs b/module/move/willbe/src/command/mod.rs index 384d1540f4..7cd16ae169 100644 --- a/module/move/willbe/src/command/mod.rs +++ b/module/move/willbe/src/command/mod.rs @@ -42,6 +42,11 @@ pub( crate ) mod private .kind( Type::Path ) .optional( true ) .end() + .property( "keep_archive" ) + .hint( "Save remote package version to the specified path" ) + .kind( Type::Path ) + .optional( true ) + .end() .routine( command::publish_diff ) .end() diff --git a/module/move/willbe/src/command/publish_diff.rs b/module/move/willbe/src/command/publish_diff.rs index baedd83605..85f0c29dc8 100644 --- a/module/move/willbe/src/command/publish_diff.rs +++ b/module/move/willbe/src/command/publish_diff.rs @@ -3,9 +3,16 @@ mod private use crate::*; use std::path::PathBuf; - use wca::Args; + use wca::{ Args, Props }; use wtools::error::Result; + use _path::AbsolutePath; + + #[ derive( former::Former ) ] + struct PublishDiffProperties + { + keep_archive : Option< PathBuf >, + } /// Command to display the differences between a local and remote package versions. /// @@ -20,14 +27,38 @@ mod private /// # Errors /// /// Returns an error if there is an issue with the command. - pub fn publish_diff( args : Args ) -> Result< () > + pub fn publish_diff( args : Args, props : Props ) -> Result< () > { let path : PathBuf = args.get_owned( 0 ).unwrap_or( std::env::current_dir()? ); + let PublishDiffProperties { keep_archive } = props.try_into()?; - println!( "{}", action::publish_diff( path )? ); + let mut o = action::PublishDiffOptions::former() + .path( path ); + if let Some( k ) = keep_archive.clone() { o = o.keep_archive( k ); } + let o = o.form(); + + println!( "{}", action::publish_diff( o )? ); + if let Some( keep ) = keep_archive + { + let keep = AbsolutePath::try_from( keep ).unwrap(); + println!( "Remote version of the package was saved at `{}`", keep.as_ref().display() ); + } Ok( () ) } + + impl TryFrom< Props > for PublishDiffProperties + { + type Error = wtools::error::for_app::Error; + fn try_from( value : Props ) -> Result< Self, Self::Error > + { + let mut this = Self::former(); + + this = if let Some( v ) = value.get_owned( "keep_archive" ) { this.keep_archive::< PathBuf >( v ) } else { this }; + + Ok( this.form() ) + } + } } // From e80fcd56d2de3c9aabc8ea4bbc0b9f83cf04ef3f Mon Sep 17 00:00:00 2001 From: SRetip Date: Fri, 22 Mar 2024 12:36:50 +0200 Subject: [PATCH 20/20] ready --- module/move/willbe/src/command/publish.rs | 2 +- module/move/willbe/src/command/test.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/module/move/willbe/src/command/publish.rs b/module/move/willbe/src/command/publish.rs index 3aa1313649..68f83df6bf 100644 --- a/module/move/willbe/src/command/publish.rs +++ b/module/move/willbe/src/command/publish.rs @@ -13,7 +13,7 @@ mod private pub fn publish( args : Args, properties : Props ) -> Result< () > { - let args_line = format!( "{}", args.get_owned( 0 ).unwrap_or( "" ) ); + let args_line = format!( "{}", args.get_owned( 0 ).unwrap_or( std::path::PathBuf::from( "" ) ).display() ); let prop_line = format!( "{}", properties.iter().map( | p | format!( "{}:{}", p.0, p.1.to_string() ) ).collect::< Vec< _ > >().join(" ") ); let patterns : Vec< _ > = args.get_owned( 0 ).unwrap_or_else( || vec![ "./".into() ] ); diff --git a/module/move/willbe/src/command/test.rs b/module/move/willbe/src/command/test.rs index a3157fa988..8146bc8ab2 100644 --- a/module/move/willbe/src/command/test.rs +++ b/module/move/willbe/src/command/test.rs @@ -52,7 +52,7 @@ mod private /// run tests in specified crate pub fn test( args : Args, properties : Props ) -> Result< () > { - let args_line = format!( "{}", args.get_owned( 0 ).unwrap_or( "" ) ); + let args_line = format!( "{}", args.get_owned( 0 ).unwrap_or( std::path::PathBuf::from( "" ) ).display() ); let prop_line = format!( "{}", properties.iter().map( | p | format!( "{}:{}", p.0, p.1.to_string() ) ).collect::< Vec< _ > >().join(" ") ); let path : PathBuf = args.get_owned( 0 ).unwrap_or_else( || "./".into() ); let path = AbsolutePath::try_from( path )?;