lift main.rs business logic into the library
The chlog target-series resolution becomes changelog::series_candidates (UNRELEASED pinning, development-series default and fallbacks modeled by SeriesCandidates), PPA references get package_info::split_ppa (shared by pull and deb, now also rejecting empty parts), and SSH endpoints get context::ContextConfig::from_endpoint — so a library consumer can resolve series, validate PPAs and build context configurations without reimplementing the CLI's rules. All three carry unit tests; main.rs keeps only parsing of flags and error handling.
This commit is contained in:
+31
-78
@@ -353,15 +353,14 @@ fn main() {
|
||||
let (pb, progress_callback) = pkh::ui::create_progress_bar(&multi);
|
||||
|
||||
// Convert PPA to base URL if provided
|
||||
let base_url = ppa.map(|ppa_str| {
|
||||
// PPA format: user/ppa_name
|
||||
let parts: Vec<&str> = ppa_str.split('/').collect();
|
||||
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
|
||||
error!("Invalid PPA format: '{}'. Expected: user/ppa_name", ppa_str);
|
||||
let base_url = match ppa.map(pkh::package_info::split_ppa) {
|
||||
Some(Ok((user, name))) => Some(pkh::package_info::ppa_to_base_url(user, name)),
|
||||
Some(Err(e)) => {
|
||||
error!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
pkh::package_info::ppa_to_base_url(parts[0], parts[1])
|
||||
});
|
||||
None => None,
|
||||
};
|
||||
|
||||
// Since pull is async, we need to block on it
|
||||
if let Err(e) = rt.block_on(async {
|
||||
@@ -395,62 +394,27 @@ fn main() {
|
||||
let target_series = if let Some(s) = cli_series {
|
||||
Some(s.to_string())
|
||||
} else {
|
||||
// Parse current changelog to determine the default series
|
||||
let changelog_path = cwd.join("debian/changelog");
|
||||
match pkh::changelog::parse_changelog_header(&changelog_path) {
|
||||
Ok((_pkg, _ver, current_series)) => {
|
||||
// UNRELEASED is not a real series: offer it as a
|
||||
// pinned first entry (selecting it keeps the changelog
|
||||
// unreleased) on top of the current vendor's series
|
||||
// list, defaulting to the development series. Any
|
||||
// other series resolves through the series list of
|
||||
// its own distribution.
|
||||
match rt.block_on(async {
|
||||
if pkh::distro_info::is_unreleased(¤t_series) {
|
||||
// Vendors keep original casing ("Ubuntu"),
|
||||
// while the series data keys are lowercase
|
||||
let dist = pkh::build::env::current_vendor().to_lowercase();
|
||||
let mut series_list =
|
||||
vec![pkh::distro_info::UNRELEASED.to_string()];
|
||||
series_list.extend(
|
||||
pkh::distro_info::get_ordered_series_name(&dist).await?,
|
||||
);
|
||||
Ok(series_list)
|
||||
} else {
|
||||
let dist =
|
||||
pkh::distro_info::get_dist_from_series(¤t_series).await?;
|
||||
pkh::distro_info::get_ordered_series_name(&dist).await
|
||||
}
|
||||
}) {
|
||||
Ok(series_list) => {
|
||||
// Default to the development series (the
|
||||
// first real entry) when the changelog is
|
||||
// UNRELEASED, not to the pinned entry itself
|
||||
let default = if pkh::distro_info::is_unreleased(¤t_series)
|
||||
&& series_list.len() > 1
|
||||
{
|
||||
series_list[1].clone()
|
||||
} else {
|
||||
current_series.clone()
|
||||
};
|
||||
match pkh::ui::select_series(&series_list, &default) {
|
||||
Ok(selected) => Some(selected),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Series selection failed: {}. Using current series '{}' instead.",
|
||||
e, current_series
|
||||
);
|
||||
Some(current_series)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// Could not fetch series list, use current series as default
|
||||
Some(current_series)
|
||||
}
|
||||
match rt.block_on(pkh::changelog::series_candidates(&changelog_path)) {
|
||||
Some(pkh::changelog::SeriesCandidates::Choose {
|
||||
options,
|
||||
default,
|
||||
fallback,
|
||||
}) => match pkh::ui::select_series(&options, &default) {
|
||||
Ok(selected) => Some(selected),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Series selection failed: {}. Using current series '{}' instead.",
|
||||
e, fallback
|
||||
);
|
||||
Some(fallback)
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
},
|
||||
// Could not fetch the series list: use the current series
|
||||
Some(pkh::changelog::SeriesCandidates::Keep(current)) => Some(current),
|
||||
// No parsable changelog: leave the series decision to
|
||||
// generate_entry
|
||||
None => None,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -668,24 +632,13 @@ fn main() {
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
// Parse host, user, port from endpoint
|
||||
// Formats: [ssh://][user@]host[:port]
|
||||
let endpoint_re = regex::Regex::new(r"^(?:ssh://)?(?:(?P<user>[^@]+)@)?(?P<host>[^:/]+)(?::(?P<port>\d+))?$").unwrap();
|
||||
let endpoint_cap = endpoint_re.captures(endpoint).unwrap_or_else(|| {
|
||||
error!("Invalid endpoint format: '{}'. Expected [ssh://][user@]host[:port]", endpoint);
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
let host = endpoint_cap.name("host").unwrap().as_str().to_string();
|
||||
let user = endpoint_cap.name("user").map(|m| m.as_str().to_string());
|
||||
let port = endpoint_cap.name("port").map(|m| {
|
||||
m.as_str().parse::<u16>().unwrap_or_else(|_| {
|
||||
error!("Invalid port number");
|
||||
match pkh::context::ContextConfig::from_endpoint(endpoint) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
std::process::exit(1);
|
||||
})
|
||||
});
|
||||
|
||||
ContextConfig::Ssh { host, user, port }
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Unknown context type: {}", type_str);
|
||||
|
||||
Reference in New Issue
Block a user