anttp 0.26.0

AntTP is an HTTP server for the Autonomi Network
use tonic::{Request, Response, Status};
use actix_web::web::Data;
use actix_multipart::form::tempfile::TempFile;
use actix_multipart::form::MultipartForm;
use std::io::Write;
use ant_core::data::Wallet;
/*use crate::service::public_archive_service::{PublicArchiveForm, Upload};*/
use crate::service::tarchive_service::TarchiveService;
use crate::controller::StoreType;
use crate::error::tarchive_error::TarchiveError;

pub mod tarchive_proto {
    tonic::include_proto!("tarchive");
}

use tarchive_proto::tarchive_service_server::TarchiveService as TarchiveServiceTrait;
pub use tarchive_proto::tarchive_service_server::TarchiveServiceServer;
use tarchive_proto::{CreateTarchiveRequest, UpdateTarchiveRequest, TruncateTarchiveRequest, TarchiveResponse, File as ProtoFile, GetTarchiveRequest, GetTarchiveResponse, Item, ListTarchiveRequest, ListTarchiveResponse, PushTarchiveRequest};
use crate::service::archive_service::{PublicArchiveForm, Upload};
use crate::service::public_data_service::PublicDataService;

pub struct TarchiveHandler {
    tarchive_service: Data<TarchiveService>,
    public_data_service: Data<PublicDataService>,
    evm_wallet: Data<Wallet>,
}

impl TarchiveHandler {
    pub fn new(tarchive_service: Data<TarchiveService>, public_data_service: Data<PublicDataService>, evm_wallet: Data<Wallet>) -> Self {
        Self { tarchive_service, public_data_service, evm_wallet }
    }

    fn map_to_multipart_form(&self, files: Vec<ProtoFile>) -> Result<MultipartForm<PublicArchiveForm>, Status> {
        let mut temp_files = Vec::new();
        for file in files {
            let mut temp_file = tempfile::NamedTempFile::new().map_err(|e|
                Status::internal(format!("Failed to create temp file: {}", e))
            )?;
            temp_file.write_all(&file.content).map_err(|e|
                Status::internal(format!("Failed to write to temp file: {}", e))
            )?;

            temp_files.push(TempFile {
                file: temp_file,
                file_name: Some(file.name),
                content_type: None,
                size: file.content.len(),
            });
        }
        Ok(MultipartForm(PublicArchiveForm { files: temp_files }))
    }
}

impl From<Upload> for TarchiveResponse {
    fn from(upload: Upload) -> Self {
        TarchiveResponse {
            address: upload.address,
        }
    }
}

impl From<TarchiveError> for Status {
    fn from(error: TarchiveError) -> Self {
        Status::internal(error.to_string())
    }
}

#[tonic::async_trait]
impl TarchiveServiceTrait for TarchiveHandler {
    async fn create_tarchive(
        &self,
        request: Request<CreateTarchiveRequest>,
    ) -> Result<Response<TarchiveResponse>, Status> {
        let req = request.into_inner();
        let tarchive_form = self.map_to_multipart_form(req.files)?;
        
        let result = self.tarchive_service.create_tarchive(
            req.path,
            tarchive_form,
            self.evm_wallet.get_ref().clone(),
            StoreType::from(req.store_type.unwrap_or_default())
        ).await?;

        Ok(Response::new(TarchiveResponse::from(result)))
    }

    async fn update_tarchive(
        &self,
        request: Request<UpdateTarchiveRequest>,
    ) -> Result<Response<TarchiveResponse>, Status> {
        let req = request.into_inner();
        let tarchive_form = self.map_to_multipart_form(req.files)?;
        
        let result = self.tarchive_service.update_tarchive(
            req.address,
            req.path,
            tarchive_form,
            self.evm_wallet.get_ref().clone(),
            StoreType::from(req.store_type.unwrap_or_default())
        ).await?;

        Ok(Response::new(TarchiveResponse::from(result)))
    }

    async fn truncate_tarchive(
        &self,
        request: Request<TruncateTarchiveRequest>,
    ) -> Result<Response<TarchiveResponse>, Status> {
        let req = request.into_inner();
        
        let result = self.tarchive_service.truncate_tarchive(
            req.address,
            req.path,
            self.evm_wallet.get_ref().clone(),
            StoreType::from(req.store_type.unwrap_or_default())
        ).await?;

        Ok(Response::new(TarchiveResponse::from(result)))
    }

    async fn get_tarchive(
        &self,
        request: Request<GetTarchiveRequest>,
    ) -> Result<Response<GetTarchiveResponse>, Status> {
        let req = request.into_inner();
        let result = self.tarchive_service.get_tarchive_binary(req.address, req.path).await?;

        let items: Vec<Item> = result.items.into_iter().map(|pd| Item {
            name: pd.display,
            modified: pd.modified,
            size: pd.size,
            r#type: format!("{:?}", pd.path_type),
        }).collect();

        Ok(Response::new(GetTarchiveResponse {
            address: Some(result.address),
            items,
            content: Some(result.content.into()),
        }))
    }

    async fn list_tarchive(
        &self,
        request: Request<ListTarchiveRequest>,
    ) -> Result<Response<ListTarchiveResponse>, Status> {
        let req = request.into_inner();
        let result = self.tarchive_service.get_tarchive(req.address, req.path).await?;

        let items: Vec<Item> = result.items.into_iter().map(|pd| Item {
            name: pd.display,
            modified: pd.modified,
            size: pd.size,
            r#type: format!("{:?}", pd.path_type),
        }).collect();

        Ok(Response::new(ListTarchiveResponse {
            address: result.address,
            items,
        }))
    }

    async fn push_tarchive(
        &self,
        request: Request<PushTarchiveRequest>,
    ) -> Result<Response<TarchiveResponse>, Status> {
        let req = request.into_inner();
        let result = self.public_data_service.push_public_data(
            req.address,
            StoreType::from(req.store_type.unwrap_or_else(|| "network".to_string()))
        ).await.map_err(|e| Status::internal(e.to_string()))?;

        Ok(Response::new(TarchiveResponse {
            address: result.address,
        }))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_mapping() {
        let upload = Upload {
            address: Some("0x1234".to_string()),
        };
        let response = TarchiveResponse::from(upload);
        assert_eq!(response.address, Some("0x1234".to_string()));
    }
}