README.md

# Spreadsheet

<!-- MDOC !-->

A fast, memory-efficient Elixir library for parsing spreadsheet files powered by Rust and [Calamine](https://docs.rs/calamine/latest/calamine/).

## Features

- 🚀 **Fast Performance** - Native Rust implementation via NIFs for high-speed parsing
- 📁 **Multiple Formats** - Supports .xls, .xla, .xlsx, .xlsm, .xlam, .xlsb, and .ods files
- 💾 **Memory Efficient** - Parse from file paths or binary content
- 🗂️ **Sheet Management** - List and filter sheet names, including hidden sheets
- 📅 **Smart Type Handling** - Automatic conversion of dates to NaiveDateTime and numbers to Float
- 🔧 **Simple API** - Clean, functional interface with {:ok, result} | {:error, reason} patterns

## Usage

### Getting Sheet Names

List all sheet names in a file:

```elixir
iex> Spreadsheet.sheet_names("financial_data.xlsx")
{:ok, ["Q1 Revenue", "Q2 Revenue", "Summary"]}
```

Or from binary content:

```elixir
iex> content = File.read!("financial_data.xlsx")
iex> Spreadsheet.sheet_names_from_binary(content)
{:ok, ["Q1 Revenue", "Q2 Revenue", "Summary"]}
```

### Filtering Hidden Sheets

Exclude hidden sheets from the results:

```elixir
iex> Spreadsheet.sheet_names("workbook.xlsx", hidden: false)
{:ok, ["Visible Sheet"]}
```

### Parsing Sheet Data

Parse a specific sheet by name:

```elixir
iex> Spreadsheet.parse("sales.xlsx", "Q1 Data")
{:ok, [
  ["Product", "Sales", "Date"],
  ["Widget A", 1500.0, ~N[2024-01-15 00:00:00]],
  ["Widget B", 2300.0, ~N[2024-01-20 00:00:00]]
]}
```

Or from binary content:

```elixir
iex> content = File.read!("sales.xlsx")
iex> Spreadsheet.parse_from_binary(content, "Q1 Data")
{:ok, [
  ["Product", "Sales", "Date"],
  ["Widget A", 1500.0, ~N[2024-01-15 00:00:00]],
  ["Widget B", 2300.0, ~N[2024-01-20 00:00:00]]
]}
```

### Data Type Handling

The library automatically converts data types:
- **Dates**: Converted to `NaiveDateTime` structs
- **Numbers**: Converted to `Float` values
- **Empty cells**: Returned as `nil`
- **Text**: Returned as strings

For detailed information on the underlying parsing engine, see the [Calamine documentation](https://docs.rs/calamine/latest/calamine/).

<!-- MDOC !-->

## Installation

Add `spreadsheet` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:spreadsheet, "~> 0.2.0"}
  ]
end
```

### No Rust Installation Required

By default, the library uses precompiled NIFs, so **you don't need Rust installed**. If you want to force compilation from source, set the application environment:

```elixir
config :rustler_precompiled, :force_build, spreadsheet: true
```

## Performance

ExSpreadsheet is built for performance and handles large files efficiently:

- **Native Speed**: Rust-powered parsing for maximum throughput
- **Memory Efficient**: Streaming approach minimizes memory usage
- **Cross-Platform**: Precompiled NIFs for major platforms (Linux, macOS, Windows)
- **Production Ready**: Battle-tested in high-volume data processing environments

## Why Spreadsheet?

Compared to other Elixir spreadsheet libraries:

- **Faster**: Native Rust implementation outperforms pure Elixir parsers
- **More Formats**: Supports more file formats including .ods and legacy .xls files
- **Better Memory Usage**: Efficient memory handling for large files
- **Zero Setup**: No need to install external dependencies

### Alternatives

- [XlsxReader](https://hex.pm/packages/xlsx_reader) - Pure Elixir, XLSX only
- [Xlsxir](https://hex.pm/packages/xlsxir) - XML-based parsing, XLSX only

## Development

### Publishing a new version

Follow the [rustler_precompiled guide](https://hexdocs.pm/rustler_precompiled/precompilation_guide.html):

1. Release a new tag
2. Push the code with the new tag: `git push origin main --tags`
3. Wait for all NIFs to be built
4. Download precompiled NIFs: `mix rustler_precompiled.download Spreadsheet.Calamine --all`
5. Release the package to Hex.pm


## Copyright and License

Copyright (c) 2025 Wilhelm H Kirschbaum

This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the [LICENSE.md](./LICENSE.md) file for more details.