zilliz 0.1.1

TUI and CLI tool for managing Zilliz Cloud clusters and Milvus operations
Documentation
use std::collections::HashMap;

use serde_json::Value;

use crate::api::client::ApiClient;
use crate::api::error::ApiError;
use crate::model::types::Operation;

/// Shared operation executor used by both CLI and TUI modes.
/// Takes an Operation definition + user-provided param values, builds the API call.
pub struct OperationExecutor<'a> {
    client: &'a ApiClient,
}

impl<'a> OperationExecutor<'a> {
    pub fn new(client: &'a ApiClient) -> Self {
        Self { client }
    }

    /// Execute an API operation with the given parameter values.
    pub async fn execute(
        &self,
        operation: &Operation,
        param_values: &HashMap<String, Value>,
    ) -> Result<Value, ApiError> {
        let mut path_params = HashMap::new();
        let mut body = serde_json::Map::new();

        // Apply body defaults first
        for (key, value) in &operation.body_defaults {
            body.insert(key.clone(), value.clone());
        }

        // Classify params into path params vs body/query params
        for param in &operation.params {
            let value = param_values.get(&param.name);

            if param.is_path_param() {
                if let Some(val) = value {
                    let str_val = match val {
                        Value::String(s) => s.clone(),
                        other => other.to_string(),
                    };
                    path_params.insert(param.name.clone(), str_val);
                }
            } else if let Some(val) = value {
                // Skip null values
                if !val.is_null() {
                    body.insert(param.name.clone(), val.clone());
                }
            }
        }

        let body_value = if body.is_empty() && operation.method() == "GET" {
            None
        } else {
            Some(Value::Object(body))
        };

        self.client
            .call(
                operation.method(),
                operation.path(),
                Some(&path_params),
                body_value.as_ref(),
            )
            .await
    }

    /// Execute a paginated list operation, fetching all pages.
    pub async fn execute_all_pages(
        &self,
        operation: &Operation,
        param_values: &HashMap<String, Value>,
    ) -> Result<Vec<Value>, ApiError> {
        let pagination = match &operation.pagination {
            Some(p) => p,
            None => return self.execute(operation, param_values).await.map(|v| vec![v]),
        };

        let mut all_items = Vec::new();
        let mut current_page = 1u64;
        let page_size = pagination.default_page_size as u64;

        loop {
            let mut page_params = param_values.clone();
            page_params.insert(
                pagination.page_size_param.clone(),
                Value::Number(page_size.into()),
            );
            page_params.insert(
                pagination.page_param.clone(),
                Value::Number(current_page.into()),
            );

            let result = self.execute(operation, &page_params).await?;

            // Extract items from the data field
            let items = result
                .get(&pagination.data_field)
                .and_then(|v| v.as_array())
                .cloned()
                .unwrap_or_default();

            let count = items.len();
            all_items.extend(items);

            // Check if we've fetched all items
            if count < page_size as usize {
                break;
            }

            current_page += 1;
        }

        Ok(all_items)
    }
}