// Copyright (C) 2025 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![allow(unused)]
//! Download module for API 9.
//!
//! This module provides functions to manage download tasks in API 9, including
//! creating, starting, pausing, resuming, and deleting download tasks, as well
//! as retrieving task information.
use std::path::PathBuf;
use ani_rs::business_error::BusinessError;
use ani_rs::objects::{AniObject, AniRef};
use ani_rs::AniEnv;
use request_client::check::file::DownloadPathError;
use request_client::client::error::CreateTaskError;
use request_client::RequestClient;
use request_core::config::{TaskConfig, Version};
use request_core::info::TaskInfo;
use request_utils::context::{is_stage_context, Context};
use super::bridge::{DownloadConfig, DownloadTask};
use crate::api9::bridge::DownloadInfo;
use crate::api9::callback::CallbackManager;
use crate::constant::*;
use crate::seq::TaskSeq;
/// Validates a download configuration and allocates a task sequence ID.
///
/// # Returns
///
/// * `Ok(i64)` containing the generated sequence ID on success
/// * `Err(BusinessError)` if the configuration or download path is invalid
#[ani_rs::native]
pub fn check_config(
env: &AniEnv,
context: AniRef,
config: DownloadConfig,
) -> Result<i64, BusinessError> {
let context = AniObject::from(context);
debug!("is {}", is_stage_context(env, &context));
// Generate a new sequential task ID for tracking
let seq = TaskSeq::next().0.get();
info!("check task, seq: {}", seq);
let context = Context::new(env, &context);
let mut config: TaskConfig = config.into();
config.bundle_type = context.get_bundle_type() as u32;
config.bundle = context.get_bundle_name();
// Create the download task
match RequestClient::get_instance().check_config(context, seq, config) {
Ok(()) => Ok(seq as i64),
Err(CreateTaskError::DownloadPath(err)) => {
let message = match err {
DownloadPathError::EmptyPath => {
"Invalid file or file system error, File path is empty".to_string()
}
DownloadPathError::TooLongPath => {
"Invalid file or file system error, File path exceeds maximum allowed length".to_string()
}
DownloadPathError::BundleNameNotMap => {
"Invalid file or file system error, File path bundle name does not match the application".to_string()
}
DownloadPathError::AlreadyExists => {
"Invalid file or file system error, File already exists, set overwrite=true to replace".to_string()
}
DownloadPathError::CreateFile(e) => {
format!("Invalid file or file system error, Failed to create file: {}", e)
}
DownloadPathError::SetPermission(e) => {
format!("Invalid file or file system error, Failed to set file permissions: {}", e)
}
DownloadPathError::AclAccess(e) => {
format!("Invalid file or file system error, ACL permission denied: {}", e)
}
_ => "Invalid file or file system error.".to_string(),
};
return Err(BusinessError::new(13400001, message))
}
Err(CreateTaskError::Code(code)) => {
return Err(BusinessError::new(code, "Download failed.".to_string()))
}
}
}
/// Creates and starts a download task with the given configuration.
///
/// # Arguments
///
/// * `env` - The animation environment reference
/// * `context` - The application context
/// * `config` - The download configuration containing URL, file path, etc.
///
/// # Returns
///
/// * `Ok(DownloadTask)` if the task was successfully created and started
/// * `Err(BusinessError)` if there was an error during task creation or start
///
/// # Errors
///
/// Returns an error if:
/// * Task creation fails
/// * Task start fails
///
/// # Examples
///
/// ```rust
/// use ani_rs::objects::AniRef;
/// use ani_rs::AniEnv;
/// use request_api9::api9::bridge::DownloadConfig;
/// use request_api9::api9::download::download_file;
///
/// // Assuming env and context are properly initialized
/// let config = DownloadConfig {
/// url: "https://example.com/file.zip".to_string(),
/// file_path: Some("./downloads/file.zip".to_string()),
/// // Other configuration fields...
/// };
///
/// match download_file(&env, context, config) {
/// Ok(task) => println!("Download started with task ID: {}", task.task_id),
/// Err(e) => println!("Error starting download: {}", e),
/// }
/// ```
#[ani_rs::native]
pub fn download_file(
env: &AniEnv,
context: AniRef,
seq: i64,
) -> Result<DownloadTask, BusinessError> {
let context = AniObject::from(context);
debug!("is {}", is_stage_context(env, &context));
let context = Context::new(env, &context);
// Create the download task
let task = match RequestClient::get_instance().create_task(context, seq as u64) {
Ok(task_id) => DownloadTask {
task_id: task_id.to_string(),
},
Err(CreateTaskError::DownloadPath(err)) => {
let message = match err {
DownloadPathError::EmptyPath => {
"Invalid file or file system error, File path is empty".to_string()
}
DownloadPathError::TooLongPath => {
"Invalid file or file system error, File path exceeds maximum allowed length".to_string()
}
DownloadPathError::BundleNameNotMap => {
"Invalid file or file system error, File path bundle name does not match the application".to_string()
}
DownloadPathError::AlreadyExists => {
"Invalid file or file system error, File already exists, set overwrite=true to replace".to_string()
}
DownloadPathError::CreateFile(e) => {
format!("Invalid file or file system error, Failed to create file: {}", e)
}
DownloadPathError::SetPermission(e) => {
format!("Invalid file or file system error, Failed to set file permissions: {}", e)
}
DownloadPathError::AclAccess(e) => {
format!("Invalid file or file system error, ACL permission denied: {}", e)
}
_ => "Invalid file or file system error.".to_string(),
};
return Err(BusinessError::new(13400001, message))
}
Err(CreateTaskError::Code(code)) => {
return Err(BusinessError::new(code, "Download failed.".to_string()))
}
};
let tid = task.task_id.parse().unwrap();
// Start the download task
match RequestClient::get_instance().start(tid) {
Ok(_) => {
info!("Api9 download started successfully, seq: {}", seq);
Ok(task)
}
Err(e) => {
error!("Api9 download start failed, error: {}", e);
Err(BusinessError::new(
e,
format!("Download start failed with error code: {}", e),
))
}
}
}
/// Deletes a download task.
///
/// Removes the specified download task from the system.
///
/// # Arguments
///
/// * `this` - The download task to delete
///
/// # Returns
///
/// * `Ok(())` if the task was successfully deleted
/// * `Err(BusinessError)` if there was an error during deletion
///
/// # Errors
///
/// Returns an error if the task cannot be deleted.
///
/// # Examples
///
/// ```rust
/// use request_api9::api9::bridge::DownloadTask;
/// use request_api9::api9::download::delete;
///
/// let task = DownloadTask { task_id: 123 };
/// match delete(task) {
/// Ok(_) => println!("Download task deleted successfully"),
/// Err(e) => println!("Error deleting task: {}", e),
/// }
/// ```
#[ani_rs::native]
pub fn delete(this: DownloadTask) -> Result<bool, BusinessError> {
let task_id = this.task_id.parse().unwrap();
match RequestClient::get_instance().remove(task_id) {
Err(e) if e == ExceptionErrorCode::E_PERMISSION as i32 => {
return Err(BusinessError::new(e, "Failed to delete download task".to_string()));
}
_ => {}
}
CallbackManager::get_instance().remove_task(task_id);
Ok(true)
}
/// Suspends a download task.
///
/// Pauses an active download task.
///
/// # Arguments
///
/// * `this` - The download task to suspend
///
/// # Returns
///
/// * `Ok(())` if the task was successfully suspended
/// * `Err(BusinessError)` if there was an error during suspension
///
/// # Errors
///
/// Returns an error if the task cannot be paused.
///
/// # Examples
///
/// ```rust
/// use request_api9::api9::bridge::DownloadTask;
/// use request_api9::api9::download::suspend;
///
/// let task = DownloadTask { task_id: 123 };
/// match suspend(task) {
/// Ok(_) => println!("Download task suspended successfully"),
/// Err(e) => println!("Error suspending task: {}", e),
/// }
/// ```
#[ani_rs::native]
pub fn suspend(this: DownloadTask) -> Result<bool, BusinessError> {
RequestClient::get_instance()
.pause(this.task_id.parse().unwrap())
.map_err(|e| {
if e != ExceptionErrorCode::E_PERMISSION as i32 {
Ok(true)
} else {
Err(BusinessError::new(
e,
"Failed to delete download task".to_string(),
))
}
});
Ok(true)
}
/// Restores a suspended download task.
///
/// Resumes a previously paused download task.
///
/// # Arguments
///
/// * `this` - The download task to restore
///
/// # Returns
///
/// * `Ok(())` if the task was successfully resumed
/// * `Err(BusinessError)` if there was an error during restoration
///
/// # Errors
///
/// Returns an error if the task cannot be resumed.
///
/// # Examples
///
/// ```rust
/// use request_api9::api9::bridge::DownloadTask;
/// use request_api9::api9::download::restore;
///
/// let task = DownloadTask { task_id: 123 };
/// match restore(task) {
/// Ok(_) => println!("Download task restored successfully"),
/// Err(e) => println!("Error restoring task: {}", e),
/// }
/// ```
#[ani_rs::native]
pub fn restore(this: DownloadTask) -> Result<bool, BusinessError> {
RequestClient::get_instance()
.resume(this.task_id.parse().unwrap())
.map_err(|e| {
if e != ExceptionErrorCode::E_PERMISSION as i32 {
Ok(true)
} else {
Err(BusinessError::new(
e,
"Failed to delete download task".to_string(),
))
}
});
Ok(true)
}
/// Retrieves information about a download task.
///
/// Gets detailed information about the specified download task.
///
/// # Arguments
///
/// * `this` - The download task to get information for
///
/// # Returns
///
/// * `Ok(DownloadInfo)` containing the task information
/// * `Err(BusinessError)` if there was an error retrieving the information
///
/// # Errors
///
/// Returns an error if the task information cannot be retrieved.
///
/// # Examples
///
/// ```rust
/// use request_api9::api9::bridge::DownloadTask;
/// use request_api9::api9::download::get_task_info;
///
/// let task = DownloadTask { task_id: 123 };
/// match get_task_info(task) {
/// Ok(info) => println!("Task status: {}", info.status),
/// Err(e) => println!("Error getting task info: {}", e),
/// }
/// ```
#[ani_rs::native]
pub fn get_task_info(this: DownloadTask) -> Result<DownloadInfo, BusinessError> {
RequestClient::get_instance()
.show_task(this.task_id.parse().unwrap())
.map(|info| DownloadInfo::from(info))
.map_err(|e| BusinessError::new(e, "Failed to get download task info".to_string()))
}
/// Gets the MIME type of a download task.
///
/// Returns the MIME type for the specified download task.
///
/// # Notes
///
/// Currently returns a static value of "application/octet-stream" for all
/// tasks.
///
/// # Arguments
///
/// * `this` - The download task to get MIME type for
///
/// # Returns
///
/// * `Ok(String)` containing the MIME type
///
/// # Examples
///
/// ```rust
/// use request_api9::api9::bridge::DownloadTask;
/// use request_api9::api9::download::get_task_mime_type;
///
/// let task = DownloadTask { task_id: 123 };
/// let mime_type = get_task_mime_type(task).unwrap();
/// println!("Task MIME type: {}", mime_type);
/// // Output: Task MIME type: application/octet-stream
/// ```
#[ani_rs::native]
pub fn get_task_mime_type(this: DownloadTask) -> Result<String, BusinessError> {
let task_id = this.task_id.parse().unwrap();
let result = RequestClient::get_instance().query_mime_type(task_id);
match result {
Ok(info) => Ok(info),
Err(e) => {
if e != ExceptionErrorCode::E_PERMISSION as i32 {
Ok("".to_string())
} else {
Err(BusinessError::new(
e,
"Failed to get task mime type".to_string(),
))
}
}
}
}