223 lines
7.9 KiB
Rust
223 lines
7.9 KiB
Rust
#![doc(hidden)]
|
|
#![expect(
|
|
clippy::needless_pass_by_value,
|
|
clippy::redundant_pub_crate,
|
|
reason = "this private runtime-planning module shares parent-only planning structs and helpers across the split CLI runtime"
|
|
)]
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use aria2_rust_pro_compat::{ConfigParseError, ConfigProfile};
|
|
use aria2_rust_pro_core::RuntimeConfig;
|
|
use aria2_rust_pro_protocol::{Downloader, HttpSessionModel, Protocol};
|
|
|
|
use super::{
|
|
CliError, CliTransferSource, ConfigLoadReport, DispatcherRegistrationKind,
|
|
InProcessRpcDispatcher, StartupProfile, TransferInputEntry, TransferSelection,
|
|
classify_transfer, derive_http_session, derive_runtime_config, expand_transfer_entries,
|
|
load_config_report, merged_profile, parse_protocol, register_resolved_entry_with_dispatcher,
|
|
resolve_transfer_entry_with_downloader,
|
|
};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(super) struct RuntimeInputContext {
|
|
pub(super) config_report: Option<ConfigLoadReport>,
|
|
pub(super) effective_profile: Option<ConfigProfile>,
|
|
pub(super) recognized_schemes: Vec<String>,
|
|
pub(super) transfer_kinds: Vec<TransferSelection>,
|
|
pub(super) derived_runtime: RuntimeConfig,
|
|
pub(super) http_session: HttpSessionModel,
|
|
pub(super) resolved_entries: Vec<ResolvedRuntimeEntry>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(super) struct ResolvedRuntimeEntry {
|
|
pub(super) entry: TransferInputEntry,
|
|
pub(super) entry_profile: Option<ConfigProfile>,
|
|
pub(super) runtime: RuntimeConfig,
|
|
pub(super) http_session: HttpSessionModel,
|
|
pub(super) uri: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(super) struct RegisteredRuntimeEntry {
|
|
pub(super) resolved: ResolvedRuntimeEntry,
|
|
pub(super) gid: String,
|
|
pub(super) registration_kind: DispatcherRegistrationKind,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct DerivedEntryContext {
|
|
runtime: RuntimeConfig,
|
|
http_session: HttpSessionModel,
|
|
}
|
|
|
|
pub(super) fn build_runtime_input_context<D: Downloader + Sync>(
|
|
config_path: Option<PathBuf>,
|
|
uris: Vec<String>,
|
|
startup: &StartupProfile,
|
|
cli_profile: Option<&ConfigProfile>,
|
|
cli_transfer_sources: Option<&[CliTransferSource]>,
|
|
downloader: &D,
|
|
) -> Result<RuntimeInputContext, CliError> {
|
|
let config_report = config_path
|
|
.as_ref()
|
|
.map(|path| load_config_report(path, true))
|
|
.transpose()?;
|
|
let file_profile = config_report.as_ref().map(|report| &report.profile);
|
|
let effective_profile = merged_profile(file_profile, cli_profile);
|
|
let profile = effective_profile.as_ref();
|
|
let input_entries = expand_transfer_entries(&uris, profile, cli_transfer_sources)?;
|
|
let first_entry_profile = input_entries.first().and_then(|entry| {
|
|
let implied = merged_profile(entry.implied_profile.as_ref(), profile);
|
|
merged_profile(implied.as_ref(), entry.profile.as_ref())
|
|
});
|
|
let report_profile = first_entry_profile.as_ref().or(profile);
|
|
let derived_runtime = derive_runtime_config(report_profile, startup);
|
|
let http_session = derive_http_session(report_profile, startup);
|
|
|
|
let recognized_schemes = input_entries
|
|
.iter()
|
|
.filter_map(|entry| entry.uris.first())
|
|
.filter_map(|uri| parse_protocol(uri).map(Protocol::as_str))
|
|
.map(str::to_owned)
|
|
.collect::<Vec<_>>();
|
|
let transfer_kinds = input_entries
|
|
.iter()
|
|
.filter_map(|entry| entry.uris.first())
|
|
.map(String::as_str)
|
|
.map(classify_transfer)
|
|
.collect::<Vec<_>>();
|
|
|
|
let resolved_entries = resolve_runtime_entries(&input_entries, startup, profile, downloader)?;
|
|
|
|
Ok(RuntimeInputContext {
|
|
config_report,
|
|
effective_profile,
|
|
recognized_schemes,
|
|
transfer_kinds,
|
|
derived_runtime,
|
|
http_session,
|
|
resolved_entries,
|
|
})
|
|
}
|
|
|
|
pub(super) fn register_runtime_entries<D: Downloader + Sync>(
|
|
dispatcher: &mut InProcessRpcDispatcher,
|
|
downloader: &D,
|
|
entries: Vec<ResolvedRuntimeEntry>,
|
|
base_profile: Option<&ConfigProfile>,
|
|
) -> Result<Vec<RegisteredRuntimeEntry>, CliError> {
|
|
let mut registered_entries = Vec::with_capacity(entries.len());
|
|
for resolved in entries {
|
|
let (gid, registration_kind) = register_resolved_entry_with_dispatcher(
|
|
dispatcher,
|
|
downloader,
|
|
&resolved.entry,
|
|
&resolved.uri,
|
|
resolved.entry_profile.as_ref().or(base_profile),
|
|
&resolved.http_session,
|
|
&resolved.runtime,
|
|
)?;
|
|
registered_entries.push(RegisteredRuntimeEntry {
|
|
resolved,
|
|
gid,
|
|
registration_kind,
|
|
});
|
|
}
|
|
Ok(registered_entries)
|
|
}
|
|
|
|
fn resolve_runtime_entries<D: Downloader + Sync>(
|
|
input_entries: &[TransferInputEntry],
|
|
startup: &StartupProfile,
|
|
profile: Option<&ConfigProfile>,
|
|
downloader: &D,
|
|
) -> Result<Vec<ResolvedRuntimeEntry>, CliError> {
|
|
let mut resolved_entries = Vec::new();
|
|
let mut derived_context_cache = Vec::new();
|
|
for entry in input_entries {
|
|
let direct_passthrough = entry
|
|
.uris
|
|
.first()
|
|
.is_some_and(|uri| classify_transfer(uri) != TransferSelection::Metalink);
|
|
let pre_resolve_profile = merged_profile(
|
|
merged_profile(entry.implied_profile.as_ref(), profile).as_ref(),
|
|
entry.profile.as_ref(),
|
|
);
|
|
let pre_resolve_context = derived_entry_context(
|
|
&mut derived_context_cache,
|
|
pre_resolve_profile.as_ref(),
|
|
startup,
|
|
);
|
|
let expanded_entries = resolve_transfer_entry_with_downloader(
|
|
entry,
|
|
downloader,
|
|
&pre_resolve_context.http_session,
|
|
&pre_resolve_context.runtime,
|
|
)?;
|
|
|
|
if direct_passthrough && expanded_entries.len() == 1 {
|
|
let resolved_uri = entry.uris.first().cloned().ok_or_else(|| {
|
|
CliError::Config(ConfigParseError::InvalidDirective(
|
|
"input-file entry missing URI".to_owned(),
|
|
))
|
|
})?;
|
|
let expanded_entry = expanded_entries
|
|
.into_iter()
|
|
.next()
|
|
.expect("direct entry fast path should preserve one resolved entry");
|
|
resolved_entries.push(ResolvedRuntimeEntry {
|
|
entry: expanded_entry,
|
|
entry_profile: pre_resolve_profile,
|
|
runtime: pre_resolve_context.runtime,
|
|
http_session: pre_resolve_context.http_session,
|
|
uri: resolved_uri,
|
|
});
|
|
continue;
|
|
}
|
|
|
|
for expanded_entry in expanded_entries {
|
|
let entry_profile = merged_profile(
|
|
merged_profile(expanded_entry.implied_profile.as_ref(), profile).as_ref(),
|
|
expanded_entry.profile.as_ref(),
|
|
);
|
|
let entry_context =
|
|
derived_entry_context(&mut derived_context_cache, entry_profile.as_ref(), startup);
|
|
let resolved_uri = expanded_entry.uris.first().cloned().ok_or_else(|| {
|
|
CliError::Config(ConfigParseError::InvalidDirective(
|
|
"input-file entry missing URI".to_owned(),
|
|
))
|
|
})?;
|
|
resolved_entries.push(ResolvedRuntimeEntry {
|
|
entry: expanded_entry,
|
|
entry_profile,
|
|
runtime: entry_context.runtime,
|
|
http_session: entry_context.http_session,
|
|
uri: resolved_uri,
|
|
});
|
|
}
|
|
}
|
|
Ok(resolved_entries)
|
|
}
|
|
|
|
fn derived_entry_context(
|
|
cache: &mut Vec<(Option<ConfigProfile>, DerivedEntryContext)>,
|
|
profile: Option<&ConfigProfile>,
|
|
startup: &StartupProfile,
|
|
) -> DerivedEntryContext {
|
|
if let Some((_, cached)) = cache
|
|
.iter()
|
|
.find(|(cached_profile, _)| cached_profile.as_ref() == profile)
|
|
{
|
|
return cached.clone();
|
|
}
|
|
|
|
let derived = DerivedEntryContext {
|
|
runtime: derive_runtime_config(profile, startup),
|
|
http_session: derive_http_session(profile, startup),
|
|
};
|
|
cache.push((profile.cloned(), derived.clone()));
|
|
derived
|
|
}
|