nylas-types 0.1.1

Type definitions for Nylas API v3
Documentation
//! ExtractAI types for intelligent data extraction.
//!
//! Extract structured data from emails using AI.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Request to extract contact information from messages.
///
/// # Example
///
/// ```
/// # use nylas_types::ExtractContactRequest;
/// let request = ExtractContactRequest {
///     message_ids: vec!["msg_123".to_string(), "msg_456".to_string()],
/// };
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExtractContactRequest {
    /// Message IDs to extract contacts from.
    pub message_ids: Vec<String>,
}

/// Response from contact extraction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExtractContactResponse {
    /// Extracted contacts indexed by message ID.
    pub contacts: HashMap<String, Vec<ExtractedContact>>,
}

/// An extracted contact from a message.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExtractedContact {
    /// Contact name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,

    /// Email address.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,

    /// Phone number.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phone: Option<String>,

    /// Company/organization.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub company: Option<String>,

    /// Job title.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,

    /// Physical address.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub address: Option<String>,

    /// Confidence score (0.0 to 1.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub confidence: Option<f64>,
}

/// Request to extract signature from messages.
///
/// # Example
///
/// ```
/// # use nylas_types::ExtractSignatureRequest;
/// let request = ExtractSignatureRequest {
///     message_ids: vec!["msg_123".to_string()],
///     parse_contacts: Some(true),
/// };
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExtractSignatureRequest {
    /// Message IDs to extract signatures from.
    pub message_ids: Vec<String>,

    /// Whether to parse contact information from signatures.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parse_contacts: Option<bool>,
}

/// Response from signature extraction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExtractSignatureResponse {
    /// Extracted signatures indexed by message ID.
    pub signatures: HashMap<String, ExtractedSignature>,
}

/// An extracted signature from a message.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExtractedSignature {
    /// Raw signature text.
    pub text: String,

    /// Parsed contact information if requested.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub contact: Option<ExtractedContact>,

    /// Confidence score (0.0 to 1.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub confidence: Option<f64>,
}

/// Request to extract orders/invoices from messages.
///
/// # Example
///
/// ```
/// # use nylas_types::ExtractOrderRequest;
/// let request = ExtractOrderRequest {
///     message_ids: vec!["msg_123".to_string()],
/// };
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExtractOrderRequest {
    /// Message IDs to extract orders from.
    pub message_ids: Vec<String>,
}

/// Response from order extraction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExtractOrderResponse {
    /// Extracted orders indexed by message ID.
    pub orders: HashMap<String, Vec<ExtractedOrder>>,
}

/// An extracted order/invoice from a message.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExtractedOrder {
    /// Order/invoice number.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_number: Option<String>,

    /// Order date (Unix timestamp).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_date: Option<i64>,

    /// Total amount.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub total: Option<f64>,

    /// Currency code (e.g., "USD").
    #[serde(skip_serializing_if = "Option::is_none")]
    pub currency: Option<String>,

    /// Merchant/vendor name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub merchant: Option<String>,

    /// Order status.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,

    /// Line items in the order.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub items: Option<Vec<OrderItem>>,

    /// Shipping information.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub shipping: Option<ShippingInfo>,

    /// Confidence score (0.0 to 1.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub confidence: Option<f64>,
}

/// A line item in an order.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OrderItem {
    /// Item name/description.
    pub name: String,

    /// Quantity.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub quantity: Option<i32>,

    /// Price per item.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub price: Option<f64>,

    /// Total price for this item.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub total: Option<f64>,

    /// SKU/product code.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sku: Option<String>,
}

/// Shipping information for an order.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ShippingInfo {
    /// Tracking number.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tracking_number: Option<String>,

    /// Carrier name.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub carrier: Option<String>,

    /// Shipping address.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub address: Option<String>,

    /// Expected delivery date (Unix timestamp).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub delivery_date: Option<i64>,
}

/// Request to extract generic entities from messages.
///
/// # Example
///
/// ```
/// # use nylas_types::ExtractEntitiesRequest;
/// let request = ExtractEntitiesRequest {
///     message_ids: vec!["msg_123".to_string()],
///     entity_types: vec!["date".to_string(), "time".to_string(), "location".to_string()],
/// };
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExtractEntitiesRequest {
    /// Message IDs to extract entities from.
    pub message_ids: Vec<String>,

    /// Types of entities to extract (e.g., "date", "time", "location", "person", "organization").
    pub entity_types: Vec<String>,
}

/// Response from entity extraction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExtractEntitiesResponse {
    /// Extracted entities indexed by message ID.
    pub entities: HashMap<String, Vec<ExtractedEntity>>,
}

/// An extracted entity from a message.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExtractedEntity {
    /// Entity type.
    pub entity_type: String,

    /// Extracted value.
    pub value: String,

    /// Original text snippet.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,

    /// Position in the message.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub position: Option<EntityPosition>,

    /// Confidence score (0.0 to 1.0).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub confidence: Option<f64>,
}

/// Position of an entity in a message.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EntityPosition {
    /// Start offset in the text.
    pub start: usize,

    /// End offset in the text.
    pub end: usize,
}

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

    #[test]
    fn test_extract_contact_request() {
        let request = ExtractContactRequest {
            message_ids: vec!["msg_123".to_string()],
        };

        let json = serde_json::to_string(&request).unwrap();
        let deserialized: ExtractContactRequest = serde_json::from_str(&json).unwrap();

        assert_eq!(request, deserialized);
    }

    #[test]
    fn test_extracted_contact() {
        let contact = ExtractedContact {
            name: Some("John Doe".to_string()),
            email: Some("john@example.com".to_string()),
            phone: Some("+1-555-0100".to_string()),
            company: Some("Acme Corp".to_string()),
            title: Some("CEO".to_string()),
            address: Some("123 Main St".to_string()),
            confidence: Some(0.95),
        };

        let json = serde_json::to_string(&contact).unwrap();
        let deserialized: ExtractedContact = serde_json::from_str(&json).unwrap();

        assert_eq!(contact, deserialized);
    }

    #[test]
    fn test_extract_signature_request() {
        let request = ExtractSignatureRequest {
            message_ids: vec!["msg_123".to_string()],
            parse_contacts: Some(true),
        };

        let json = serde_json::to_string(&request).unwrap();
        let deserialized: ExtractSignatureRequest = serde_json::from_str(&json).unwrap();

        assert_eq!(request, deserialized);
    }

    #[test]
    fn test_extracted_order() {
        let order = ExtractedOrder {
            order_number: Some("ORD-12345".to_string()),
            order_date: Some(1735689600),
            total: Some(99.99),
            currency: Some("USD".to_string()),
            merchant: Some("Example Store".to_string()),
            status: Some("shipped".to_string()),
            items: Some(vec![OrderItem {
                name: "Product A".to_string(),
                quantity: Some(2),
                price: Some(49.99),
                total: Some(99.98),
                sku: Some("SKU-001".to_string()),
            }]),
            shipping: Some(ShippingInfo {
                tracking_number: Some("1Z999AA10123456784".to_string()),
                carrier: Some("UPS".to_string()),
                address: Some("456 Oak Ave".to_string()),
                delivery_date: Some(1735776000),
            }),
            confidence: Some(0.92),
        };

        let json = serde_json::to_string(&order).unwrap();
        let deserialized: ExtractedOrder = serde_json::from_str(&json).unwrap();

        assert_eq!(order, deserialized);
    }

    #[test]
    fn test_extract_entities_request() {
        let request = ExtractEntitiesRequest {
            message_ids: vec!["msg_123".to_string()],
            entity_types: vec!["date".to_string(), "location".to_string()],
        };

        let json = serde_json::to_string(&request).unwrap();
        let deserialized: ExtractEntitiesRequest = serde_json::from_str(&json).unwrap();

        assert_eq!(request, deserialized);
    }

    #[test]
    fn test_extracted_entity() {
        let entity = ExtractedEntity {
            entity_type: "date".to_string(),
            value: "2025-01-01".to_string(),
            text: Some("January 1st, 2025".to_string()),
            position: Some(EntityPosition { start: 10, end: 27 }),
            confidence: Some(0.98),
        };

        let json = serde_json::to_string(&entity).unwrap();
        let deserialized: ExtractedEntity = serde_json::from_str(&json).unwrap();

        assert_eq!(entity, deserialized);
    }
}