Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quick Start

Get up and running with xportrs in 5 minutes.

Installation

Add xportrs to your Cargo.toml:

[dependencies]
xportrs = "0.0.8"

Reading an XPT File

use xportrs::Xpt;

fn main() -> xportrs::Result<()> {
    // Read an XPT file
    let dataset = Xpt::read("ae.xpt")?;
    
    // Basic info
    println!("Domain: {}", dataset.domain_code());
    println!("Rows: {}", dataset.nrows());
    println!("Columns: {}", dataset.ncols());
    
    // List columns
    for col in dataset.columns() {
        println!("  - {}", col.name());
    }
    
    Ok(())
}

Creating a Dataset

use xportrs::{Column, ColumnData, Dataset};

fn main() -> xportrs::Result<()> {
let dataset = Dataset::new("AE", vec![
    Column::new("USUBJID", ColumnData::String(vec![
        Some("001".into()),
        Some("002".into()),
        Some("003".into()),
    ])),
    Column::new("AESEQ", ColumnData::F64(vec![
        Some(1.0),
        Some(1.0),
        Some(2.0),
    ])),
    Column::new("AETERM", ColumnData::String(vec![
        Some("HEADACHE".into()),
        Some("NAUSEA".into()),
        Some("FATIGUE".into()),
    ])),
])?;

println!("Created {} with {} rows", dataset.domain_code(), dataset.nrows());
Ok(())
}

Writing an XPT File

use xportrs::{Column, ColumnData, Dataset, Xpt};

fn main() -> xportrs::Result<()> {
let dataset = Dataset::new("AE", vec![
    Column::new("USUBJID", ColumnData::String(vec![Some("001".into())])),
    Column::new("AESEQ", ColumnData::F64(vec![Some(1.0)])),
])?;

// Write to file
Xpt::writer(dataset)
    .finalize()?
    .write_path("ae_output.xpt")?;

println!("Wrote ae_output.xpt");
Ok(())
}

Adding Metadata

For regulatory submissions, include metadata:

use xportrs::{Column, ColumnData, Dataset, Format, Xpt};

fn main() -> xportrs::Result<()> {
let dataset = Dataset::with_label("AE", "Adverse Events", vec![
    Column::new("USUBJID", ColumnData::String(vec![Some("001".into())]))
        .with_label("Unique Subject Identifier")
        .with_format(Format::character(40)),
    
    Column::new("AESEQ", ColumnData::F64(vec![Some(1.0)]))
        .with_label("Sequence Number")
        .with_format(Format::numeric(8, 0)),
    
    Column::new("AETERM", ColumnData::String(vec![Some("HEADACHE".into())]))
        .with_label("Reported Term for the Adverse Event")
        .with_format(Format::character(200))
        .with_length(200),
])?;

Xpt::writer(dataset)
    .finalize()?
    .write_path("ae_metadata.xpt")?;
Ok(())
}

FDA Validation

Validate for FDA submission:

use xportrs::{Agency, Column, ColumnData, Dataset, Xpt};

fn main() -> xportrs::Result<()> {
let dataset = Dataset::new("AE", vec![
    Column::new("USUBJID", ColumnData::String(vec![Some("001".into())])),
])?;
let validated = Xpt::writer(dataset)
    .agency(Agency::FDA)
    .finalize()?;

// Check for issues
if validated.has_errors() {
    eprintln!("Validation errors:");
    for issue in validated.issues() {
        eprintln!("  {}", issue);
    }
    return Ok(());
}

if validated.has_warnings() {
    println!("Warnings (proceeding anyway):");
    for issue in validated.issues() {
        println!("  {}", issue);
    }
}

validated.write_path("ae.xpt")?;
Ok(())
}

Round-Trip (Read → Modify → Write)

use xportrs::Xpt;

fn main() -> xportrs::Result<()> {
    // Read existing file
    let dataset = Xpt::read("ae.xpt")?;
    
    // Modify (example: add column)
    // dataset.extend([new_column]);
    
    // Write back
    Xpt::writer(dataset)
        .finalize()?
        .write_path("ae_modified.xpt")?;
    
    Ok(())
}

Common Patterns

Using From Conversions

use xportrs::{Column, ColumnData, Dataset};

fn main() -> xportrs::Result<()> {
// Simpler syntax with From implementations
let dataset = Dataset::new("LB", vec![
    Column::new("LBSEQ", vec![1.0, 2.0, 3.0].into()),  // Vec<f64> → ColumnData
    Column::new("LBTEST", vec!["HGB", "WBC", "PLT"].into()),  // Vec<&str> → ColumnData
])?;
Ok(())
}

Accessing Column Data

use xportrs::{ColumnData, Xpt};

fn main() -> xportrs::Result<()> {
let dataset = Xpt::read("ae.xpt")?;

// By name
let col = &dataset["USUBJID"];

// Match on data type
match col.data() {
    ColumnData::String(values) => {
        for (i, val) in values.iter().enumerate() {
            match val {
                Some(s) => println!("Row {}: {}", i, s),
                None => println!("Row {}: <missing>", i),
            }
        }
    }
    ColumnData::F64(values) => {
        for (i, val) in values.iter().enumerate() {
            match val {
                Some(n) => println!("Row {}: {}", i, n),
                None => println!("Row {}: <missing>", i),
            }
        }
    }
    _ => {}
}
Ok(())
}

Handling Errors

use xportrs::{Error, Xpt};

match Xpt::read("missing.xpt") {
    Ok(dataset) => println!("Loaded"),
    Err(Error::Io(e)) => eprintln!("File error: {}", e),
    Err(e) => eprintln!("Error: {}", e),
}

Next Steps