Skip to main content

MultipartData

Struct MultipartData 

Source
pub struct MultipartData {
    pub fields: Vec<Field>,
}
Expand description

Container for all multipart form data fields.

MultipartData provides convenient access methods for working with collections of multipart fields. It allows you to query fields by name, filter by type (text vs. file), and iterate over all fields.

This container is returned by Multipart::collect_all() and provides a higher-level interface for accessing parsed multipart data.

§Field Access Patterns

The container provides several ways to access fields:

  • By name: Get specific fields using get_field() or get_fields()
  • By type: Filter text fields with text_fields() or files with file_fields()
  • Direct access: Iterate over all fields using fields

§Examples

§Accessing Specific Fields

use ignitia::multipart::MultipartData;

async fn process_form(data: MultipartData) -> Result<(), Box<dyn std::error::Error>> {
    // Get a specific field by name
    if let Some(email_field) = data.get_field("email") {
        let email = email_field.text().await?;
        println!("Email: {}", email);
    }

    // Get all fields with the same name (e.g., multiple file uploads)
    let photo_fields = data.get_fields("photos");
    for photo in photo_fields {
        if let Some(filename) = photo.file_name() {
            println!("Photo: {}", filename);
        }
    }

    Ok(())
}

§Processing by Field Type

async fn categorize_fields(data: MultipartData) -> Result<(), Box<dyn std::error::Error>> {
    // Process all text fields
    println!("Text fields:");
    for field in data.text_fields() {
        let value = field.text().await?;
        println!("  {}: {}", field.name(), value);
    }

    // Process all file uploads
    println!("File uploads:");
    for field in data.file_fields() {
        let filename = field.file_name().unwrap_or("unnamed");
        println!("  {}: {} ({})", field.name(), filename,
            field.content_type().unwrap_or("unknown"));
    }

    Ok(())
}

Fields§

§fields: Vec<Field>

Vector containing all parsed fields in order

Implementations§

Source§

impl MultipartData

Source

pub fn get_fields(&self, name: &str) -> Vec<&Field>

Returns all fields that have the specified name.

This method is useful when multiple fields share the same name, such as multiple file uploads or checkbox values. It returns references to all matching fields in the order they appeared in the multipart data.

§Arguments
  • name - The field name to search for
§Returns

A vector of references to fields with the matching name. Returns an empty vector if no fields match.

§Examples
use ignitia::multipart::MultipartData;

async fn process_photos(data: MultipartData) -> Result<(), Box<dyn std::error::Error>> {
    // Get all photo uploads (multiple files with same field name)
    let photos = data.get_fields("photos");

    println!("Found {} photos", photos.len());

    for (i, photo) in photos.iter().enumerate() {
        let filename = photo.file_name().unwrap_or(&format!("photo_{}", i));
        println!("Photo {}: {}", i + 1, filename);

        // Save each photo
        let saved = photo.save_to_file(format!("uploads/{}", filename)).await?;
        println!("  Saved {} bytes", saved.size);
    }

    Ok(())
}
§Performance

This method performs a linear search through all fields, so its performance is O(n) where n is the total number of fields.

Source

pub fn get_field(&self, name: &str) -> Option<&Field>

Returns the first field with the specified name.

This is a convenience method for accessing single-valued fields where you expect only one field with the given name. If multiple fields have the same name, only the first one is returned.

§Arguments
  • name - The field name to search for
§Returns
  • Some(field) - Reference to the first field with the matching name
  • None - No field found with the specified name
§Examples
use ignitia::multipart::MultipartData;

async fn get_user_info(data: MultipartData) -> Result<(), Box<dyn std::error::Error>> {
    // Get individual form fields
    let username = match data.get_field("username") {
        Some(field) => field.text().await?,
        None => return Err("Username field is required".into()),
    };

    let email = match data.get_field("email") {
        Some(field) => field.text().await?,
        None => return Err("Email field is required".into()),
    };

    // Optional profile picture
    if let Some(profile_pic) = data.get_field("profile_picture") {
        if profile_pic.is_file() {
            let saved_file = profile_pic.save_to_file("uploads/profile.jpg").await?;
            println!("Profile picture saved: {} bytes", saved_file.size);
        }
    }

    println!("User: {} ({})", username, email);
    Ok(())
}
§Use Cases
  • Single-value form fields (username, email, etc.)
  • Optional file uploads
  • Configuration parameters
  • Any field where you expect at most one value
Source

pub fn text_fields(&self) -> Vec<&Field>

Returns all fields that are text fields (not file uploads).

This method filters the fields to return only those that represent regular form inputs like text boxes, textareas, hidden fields, etc. It excludes any fields that represent file uploads.

§Returns

A vector of references to all text fields in the order they appeared in the multipart data.

§Examples
§Processing Form Data
use ignitia::multipart::MultipartData;
use std::collections::HashMap;

async fn extract_form_data(data: MultipartData) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
    let mut form_data = HashMap::new();

    // Convert all text fields to a key-value map
    for field in data.text_fields() {
        let name = field.name().to_string();
        let value = field.text().await?;
        form_data.insert(name, value);
    }

    Ok(form_data)
}
§Validation
async fn validate_required_fields(data: MultipartData) -> Result<(), String> {
    let required_fields = vec!["username", "email", "password"];
    let text_fields = data.text_fields();

    for required in required_fields {
        let found = text_fields.iter().any(|field| field.name() == required);
        if !found {
            return Err(format!("Required field '{}' is missing", required));
        }
    }

    Ok(())
}
§Field Classification

A field is considered a text field if:

  • It does not have a filename in its Content-Disposition header
  • It is not stored as a temporary file on disk

This includes:

  • Regular text inputs
  • Hidden fields
  • Textareas
  • Select boxes
  • Radio buttons and checkboxes
Source

pub fn file_fields(&self) -> Vec<&Field>

Returns all fields that represent file uploads.

This method filters the fields to return only those that represent file uploads, identified by having a filename in the Content-Disposition header or being stored as temporary files due to their size.

§Returns

A vector of references to all file fields in the order they appeared in the multipart data.

§Examples
§Processing File Uploads
use ignitia::multipart::MultipartData;

async fn save_all_uploads(data: MultipartData) -> Result<(), Box<dyn std::error::Error>> {
    let uploads_dir = "uploads";
    std::fs::create_dir_all(uploads_dir)?;

    let mut saved_files = Vec::new();

    for (i, field) in data.file_fields().iter().enumerate() {
        let filename = field.file_name()
            .map(|s| s.to_string())
            .unwrap_or_else(|| format!("upload_{}", i));

        let file_path = format!("{}/{}", uploads_dir, filename);
        let saved_file = field.save_to_file(&file_path).await?;

        saved_files.push(saved_file);
        println!("Saved: {} ({} bytes)", file_path, saved_file.size);
    }

    println!("Total files saved: {}", saved_files.len());
    Ok(())
}
§File Analysis
async fn analyze_uploads(data: MultipartData) -> Result<(), Box<dyn std::error::Error>> {
    let file_fields = data.file_fields();

    println!("Upload Summary:");
    println!("  Total files: {}", file_fields.len());

    let mut total_size = 0u64;
    let mut content_types = std::collections::HashMap::new();

    for field in file_fields {
        // Get file size
        let bytes = field.bytes().await?;
        total_size += bytes.len() as u64;

        // Track content types
        let content_type = field.content_type().unwrap_or("unknown");
        *content_types.entry(content_type).or_insert(0) += 1;

        println!("  - {} ({}): {} bytes",
            field.name(),
            field.file_name().unwrap_or("unnamed"),
            bytes.len()
        );
    }

    println!("  Total size: {} bytes", total_size);
    println!("  Content types: {:?}", content_types);

    Ok(())
}
§File Detection

A field is considered a file upload if:

  • It has a filename parameter in its Content-Disposition header, OR
  • It was written to a temporary file due to exceeding the memory threshold

This covers:

  • Explicit file uploads via <input type="file">
  • Large text fields that overflow to disk storage
  • Binary data submitted through form fields

Trait Implementations§

Source§

impl Debug for MultipartData

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more