#[ allow( clippy ::std_instead_of_alloc, clippy ::std_instead_of_core ) ]
mod private
{
use crate :: *;
use collection_tools :: collection;
use std :: { env, fmt, fs };
use
{
error ::ErrWith,
};
#[ derive( Debug, Default, Clone ) ]
pub struct PublishReport
{
pub workspace_root_dir: Option< AbsolutePath >,
pub plan: Option< publish ::PublishPlan >,
pub packages: Vec< ( AbsolutePath, publish ::PublishReport ) >
}
impl fmt ::Display for PublishReport
{
fn fmt( &self, f: &mut fmt ::Formatter< '_ > ) -> fmt ::Result
{
if self.packages.is_empty()
{
write!( f, "Nothing to publish" )?;
return std ::fmt ::Result ::Ok( () );
}
writeln!( f, "Actions: " )?;
for ( path, report ) in &self.packages
{
let report = report.to_string().replace( '\n', "\n " );
let path = if let Some( wrd ) = &self.workspace_root_dir
{
path.as_ref().strip_prefix( wrd.as_ref() ).unwrap()
}
else
{
path.as_ref()
};
write!( f, "Publishing crate by `{}` path\n {report}", path.display() )?;
}
if let Some( plan ) = &self.plan
{
if !plan.dry
{
let expected_to_publish: Vec< _ > = plan
.plans
.iter()
.map
(
| p |
(
p.bump.crate_dir.clone().absolute_path(),
p.package_name.clone(),
p.bump.clone()
)
)
.collect();
let mut actually_published: Vec< _ > = self.packages.iter()
.filter_map
(
| ( path, repo ) |
if repo.publish.as_ref().is_some_and( | r | r.error.is_ok() )
{
Some( path.clone() )
}
else
{
None
}
)
.collect();
writeln!( f, "Status: " )?;
for ( path, name, version ) in expected_to_publish
{
if let Some( pos ) = actually_published
.iter()
.position( | p | p == &path )
{
writeln!( f, "✅ {name} {}", version.new_version )?;
_ = actually_published.remove( pos );
}
else
{
writeln!( f, "❌ {name} {}", version.old_version )?;
}
}
if !actually_published.is_empty()
{
writeln!( f, "Logical error. Published unexpected packages" )?;
return Err( std ::fmt ::Error );
}
}
}
std ::fmt ::Result ::Ok( () )
}
}
#[ cfg_attr( feature = "tracing", tracing ::instrument ) ]
pub fn publish_plan
(
patterns: &[ String ],
channel: channel ::Channel,
dry: bool,
temp: bool
)
-> Result< publish ::PublishPlan, error ::untyped ::Error >
{
let mut paths = collection ::HashSet ::new();
for pattern in patterns
{
let current_path = AbsolutePath ::try_from
(
fs ::canonicalize( pattern.as_str() )?
)?;
paths.extend( Some( current_path ) );
}
let workspace = if paths.is_empty()
{
Workspace ::try_from( CurrentPath )?
}
else
{
let current_path = paths.iter().next().unwrap().clone();
let dir = CrateDir ::try_from( current_path )?;
Workspace ::try_from( dir )?
};
let workspace_root_dir: AbsolutePath = workspace
.workspace_root()
.into();
let packages = workspace.packages();
let packages_to_publish: Vec< String > = packages
.clone()
.filter( | &package | paths.contains( &AbsolutePath ::from( package.crate_dir().unwrap() ) ) )
.map( | p | p.name().to_string() )
.collect();
let package_map: collection ::HashMap< String, package ::Package< '_ > > = packages
.map( | p | ( p.name().to_string(), package ::Package ::from( p ) ) )
.collect();
let graph = workspace_graph ::graph( &workspace );
let subgraph_wanted = graph ::subgraph
(
&graph,
&packages_to_publish[ .. ]
);
let tmp_subgraph = subgraph_wanted
.map
(
| _, n |
graph[ *n ].clone(), | _, e | graph[ *e ].clone()
);
let mut unique_name = format!
(
"temp_dir_for_publish_command_{}",
path ::unique_folder_name()?
);
let dir = if temp
{
let mut temp_dir = env ::temp_dir().join( unique_name );
while temp_dir.exists()
{
unique_name = format!
(
"temp_dir_for_publish_command_{}",
path ::unique_folder_name()?
);
temp_dir = env ::temp_dir().join( unique_name );
}
fs ::create_dir( &temp_dir )?;
Some( temp_dir )
}
else
{
None
};
let subgraph = graph ::remove_not_required_to_publish
(
&workspace,
&package_map,
&tmp_subgraph,
&packages_to_publish,
dir.clone(),
)?;
let subgraph = subgraph
.map( | _, n | n, | _, e | e );
let queue: Vec< _ > = graph ::toposort( subgraph )
.unwrap()
.into_iter()
.map( | n | package_map.get( &n ).unwrap() )
.cloned()
.collect();
let roots: Vec< _ > = packages_to_publish
.iter()
.map( | p | package_map.get( p ).unwrap().crate_dir() ).collect();
let plan = publish ::PublishPlan ::former()
.channel( channel )
.workspace_dir( CrateDir ::try_from( workspace_root_dir ).unwrap() )
.option_base_temp_dir( dir.clone() )
.dry( dry )
.roots( roots )
.packages( queue )
.form();
Ok( plan )
}
#[ allow( clippy ::result_large_err ) ]
#[ cfg_attr( feature = "tracing", tracing ::instrument ) ]
pub fn publish( plan: publish ::PublishPlan )
->
ResultWithReport< PublishReport, error ::untyped ::Error >
{
let mut report = PublishReport ::default();
let temp = plan.base_temp_dir.clone();
report.plan = Some( plan.clone() );
for package_report in publish ::perform_packages_publish( plan ).err_with_report( &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 ) );
}
if let Some( dir ) = temp
{
fs ::remove_dir_all( dir ).err_with_report( &report )?;
}
Result ::Ok( report )
}
}
crate ::mod_interface!
{
orphan use publish_plan;
orphan use publish;
}