use super::*; pub(super) fn run_readonly_query_command(options: &CliOptions) -> anyhow::Result<()> { run_readonly_query_command_with_rpc(options, daemon_rpc_available, daemon_rpc_call) } pub(super) fn run_readonly_query_command_with_rpc( options: &CliOptions, rpc_available: impl Fn(&Path) -> bool, rpc_call: impl Fn(&Path, &str, Option) -> anyhow::Result, ) -> anyhow::Result<()> { let method = readonly_query_rpc_method(options.command) .ok_or_else(|| anyhow::anyhow!("不是只读查询命令"))?; if rpc_available(&options.state_dir) && !readonly_query_requires_local_config(options) { let _control_lock = DaemonControlLock::acquire(&options.state_dir)?; let report = rpc_call( &options.state_dir, method, readonly_query_rpc_params(options), )?; print_json_value(options.output_format, &report)?; return Ok(()); } let report = build_readonly_query_report(options, method)?; print_json_value(options.output_format, &report) } fn readonly_query_rpc_method(command: CliCommand) -> Option<&'static str> { match command { CliCommand::ParseStatus => Some(RPC_METHOD_PARSE_STATUS), CliCommand::ParseTextUnits => Some(RPC_METHOD_PARSE_TEXT_UNITS), CliCommand::ParseErrors => Some(RPC_METHOD_PARSE_ERRORS), CliCommand::TranslationTasks => Some(RPC_METHOD_TRANSLATION_TASKS), CliCommand::TranslationHandoff => Some(RPC_METHOD_TRANSLATION_HANDOFF), CliCommand::LocalizedStatus => Some(RPC_METHOD_LOCALIZED_STATUS), CliCommand::ResourceIndex => Some(RPC_METHOD_RESOURCE_INDEX), _ => None, } } fn readonly_query_rpc_params(options: &CliOptions) -> Option { let mut params = serde_json::Map::new(); match options.command { CliCommand::ResourceIndex | CliCommand::ParseTextUnits | CliCommand::ParseErrors | CliCommand::TranslationTasks => { params.insert( "offset".to_string(), serde_json::json!(options.query_offset), ); params.insert("limit".to_string(), serde_json::json!(options.query_limit)); } _ => return None, } match options.command { CliCommand::ResourceIndex => { if let Some(resource_type) = options.query_resource_type { params.insert( "resource_type".to_string(), serde_json::json!(resource_type_rpc_label(resource_type)), ); } if let Some(hash) = options.query_hash.as_ref() { params.insert("hash".to_string(), serde_json::json!(hash)); } if let Some(path_pattern) = options.query_path_pattern.as_ref() { params.insert("path_pattern".to_string(), serde_json::json!(path_pattern)); } if let Some(release_id) = options.query_official_release_id.as_ref() { params.insert( "official_release_id".to_string(), serde_json::json!(release_id), ); } if let Some(platform) = options.query_platform.as_ref() { params.insert("platform".to_string(), serde_json::json!(platform)); } if let Some(destination) = options.query_destination.as_ref() { params.insert("destination".to_string(), serde_json::json!(destination)); } if let Some(bundle_path) = options.query_bundle_path.as_ref() { params.insert("bundle_path".to_string(), serde_json::json!(bundle_path)); } if let Some(archive_entry) = options.query_archive_entry.as_ref() { params.insert( "archive_entry".to_string(), serde_json::json!(archive_entry), ); } if let Some(parse_status) = options.query_parse_status.as_ref() { params.insert("parse_status".to_string(), serde_json::json!(parse_status)); } if let Some(format) = options.query_format.as_ref() { params.insert("text_unit_format".to_string(), serde_json::json!(format)); } } CliCommand::ParseTextUnits | CliCommand::ParseErrors => { if let Some(destination) = options.query_destination.as_ref() { params.insert("destination".to_string(), serde_json::json!(destination)); } if let Some(path_pattern) = options.query_path_pattern.as_ref() { params.insert("path_pattern".to_string(), serde_json::json!(path_pattern)); } if let Some(archive_entry) = options.query_archive_entry.as_ref() { params.insert( "archive_entry".to_string(), serde_json::json!(archive_entry), ); } if let Some(path_id) = options.query_path_id { params.insert("path_id".to_string(), serde_json::json!(path_id)); } if let Some(class_id) = options.query_class_id { params.insert("class_id".to_string(), serde_json::json!(class_id)); } if let Some(field_path) = options.query_field_path.as_ref() { params.insert("field_path".to_string(), serde_json::json!(field_path)); } if let Some(format) = options.query_format.as_ref() { params.insert("format".to_string(), serde_json::json!(format)); } } CliCommand::TranslationTasks => { if let Some(task_id) = options.query_task_id.as_ref() { params.insert("task_id".to_string(), serde_json::json!(task_id)); } if let Some(release_id) = options.query_official_release_id.as_ref() { params.insert( "official_release_id".to_string(), serde_json::json!(release_id), ); } if let Some(destination) = options.query_destination.as_ref() { params.insert("destination".to_string(), serde_json::json!(destination)); } if let Some(path_pattern) = options.query_path_pattern.as_ref() { params.insert("path_pattern".to_string(), serde_json::json!(path_pattern)); } if let Some(archive_entry) = options.query_archive_entry.as_ref() { params.insert( "archive_entry".to_string(), serde_json::json!(archive_entry), ); } if let Some(status) = options.query_task_status.as_ref() { params.insert("status".to_string(), serde_json::json!(status)); } if let Some(status) = options.query_worker_status.as_ref() { params.insert("worker_status".to_string(), serde_json::json!(status)); } if let Some(parse_status) = options.query_parse_status.as_ref() { params.insert("parse_status".to_string(), serde_json::json!(parse_status)); } if let Some(format) = options.query_format.as_ref() { params.insert("text_unit_format".to_string(), serde_json::json!(format)); } if let Some(has_reason) = options.query_has_reason { params.insert("has_reason".to_string(), serde_json::json!(has_reason)); } if let Some(has_failure_reason) = options.query_has_failure_reason { params.insert( "has_failure_reason".to_string(), serde_json::json!(has_failure_reason), ); } } _ => {} } Some(serde_json::Value::Object(params)) } fn readonly_query_requires_local_config(options: &CliOptions) -> bool { matches!(options.command, CliCommand::ResourceIndex) && options.config.import_resource_repository_path.is_some() } fn build_readonly_query_report( options: &CliOptions, method: &str, ) -> anyhow::Result { match method { RPC_METHOD_PARSE_STATUS => build_parse_status_report(&options.state_dir), RPC_METHOD_PARSE_TEXT_UNITS => build_parse_text_units_report( &options.state_dir, textunit_query_from_options(options), options.query_offset, options.query_limit, ), RPC_METHOD_PARSE_ERRORS => build_parse_errors_report( &options.state_dir, textunit_query_from_options(options), options.query_offset, options.query_limit, ), RPC_METHOD_TRANSLATION_TASKS => build_translation_tasks_report( &options.state_dir, translation_task_query_from_options(options), options.query_offset, options.query_limit, ), RPC_METHOD_TRANSLATION_HANDOFF => build_translation_handoff_report(&options.state_dir), RPC_METHOD_LOCALIZED_STATUS => { build_localized_status_report(&options.state_dir, &options.config) } RPC_METHOD_RESOURCE_INDEX => build_resource_index_report( &options.state_dir, &options.config, resource_index_query_from_options(options), options.query_offset, options.query_limit, ), _ => Err(anyhow::anyhow!("不支持的只读查询方法:{method}")), } } pub(super) fn validate_readonly_query_options(options: &CliOptions) -> anyhow::Result<()> { let has_resource_index_only_filter = options.query_resource_type.is_some() || options.query_hash.is_some() || options.query_platform.is_some() || options.query_bundle_path.is_some(); let has_parse_object_filter = options.query_path_id.is_some() || options.query_class_id.is_some() || options.query_field_path.is_some(); let has_translation_task_filter = options.query_task_id.is_some() || options.query_task_status.is_some() || options.query_worker_status.is_some() || options.query_has_reason.is_some() || options.query_has_failure_reason.is_some(); match options.command { CliCommand::ResourceIndex => { if has_parse_object_filter || has_translation_task_filter { return Err(anyhow::anyhow!( "--path-id/--class-id/--field-path 只适用于 parse-text-units 或 parse-errors;--task-id/--task-status/--worker-status/--has-reason/--has-failure-reason 只适用于 translation-tasks" )); } } CliCommand::ParseTextUnits | CliCommand::ParseErrors => { if has_resource_index_only_filter || has_translation_task_filter || options.query_official_release_id.is_some() || options.query_parse_status.is_some() { return Err(anyhow::anyhow!( "--resource-type/--hash/--release-id/--platform/--bundle-path/--parse-status 只适用于 resource-index 或 translation-tasks;--task-id/--task-status/--worker-status/--has-reason/--has-failure-reason 只适用于 translation-tasks" )); } } CliCommand::TranslationTasks => { if has_resource_index_only_filter || has_parse_object_filter { return Err(anyhow::anyhow!( "--resource-type/--hash/--platform/--bundle-path 只适用于 resource-index;--path-id/--class-id/--field-path 只适用于 parse-text-units 或 parse-errors" )); } } CliCommand::TranslationHandoff if options.query_option_explicit => { return Err(anyhow::anyhow!( "translation-handoff 不接受查询过滤参数;请使用 translation-tasks 查询单项任务" )); } CliCommand::ParseStatus | CliCommand::LocalizedStatus if options.query_option_explicit => { return Err(anyhow::anyhow!( "查询过滤参数只适用于 resource-index、parse-text-units、parse-errors 或 translation-tasks" )); } _ => {} } Ok(()) }