Xbase

Hex.pmDocumentationLicense

A comprehensive, production-ready Elixir library for reading, writing, and manipulating dBase database files (DBF) with full support for memo fields (DBT) and indexes (CDX).

Built for performance, reliability, and ease of use, Xbase provides a complete solution for working with legacy dBase files in modern Elixir applications.

✨ Features

🗃️ Complete dBase Support

📝 Advanced Memo Fields

🚀 Performance & Scalability

🔐 Enterprise Ready

📦 Installation

Add xbase to your list of dependencies in mix.exs:

def deps do
  [
    {:xbase, "~> 0.1.0"}
  ]
end

Then run:

mix deps.get

🚀 Quick Start

Reading DBF Files

# Open a DBF file
{:ok, dbf} = Xbase.Parser.open_dbf("data.dbf")

# Read a single record
{:ok, record} = Xbase.Parser.read_record(dbf, 0)
# => %Xbase.Types.Record{data: %{"NAME" => "John Doe", "AGE" => 30}, deleted: false}

# Read all records
{:ok, records} = Xbase.Parser.read_records(dbf)

# Stream records for memory efficiency
dbf
|> Xbase.Parser.stream_records()
|> Stream.filter(fn record -> record.data["AGE"] > 25 end)
|> Enum.to_list()

# Don't forget to close
Xbase.Parser.close_dbf(dbf)

Writing DBF Files

# Define field structure
fields = [
  %Xbase.Types.FieldDescriptor{name: "NAME", type: "C", length: 30},
  %Xbase.Types.FieldDescriptor{name: "AGE", type: "N", length: 3, decimal_count: 0},
  %Xbase.Types.FieldDescriptor{name: "BIRTHDATE", type: "D", length: 8},
  %Xbase.Types.FieldDescriptor{name: "ACTIVE", type: "L", length: 1}
]

# Create new DBF file
{:ok, dbf} = Xbase.Parser.create_dbf("output.dbf", fields)

# Append records
{:ok, dbf} = Xbase.Parser.append_record(dbf, %{
  "NAME" => "Jane Smith",
  "AGE" => 28,
  "BIRTHDATE" => ~D[1995-03-15],
  "ACTIVE" => true
})

# Update existing record
{:ok, dbf} = Xbase.Parser.update_record(dbf, 0, %{"AGE" => 29})

# Mark record as deleted
{:ok, dbf} = Xbase.Parser.mark_deleted(dbf, 0)

# Pack file to remove deleted records
{:ok, dbf} = Xbase.Parser.pack(dbf, "packed.dbf")

Xbase.Parser.close_dbf(dbf)

Working with Memo Fields

# Using MemoHandler for seamless memo support
{:ok, handler} = Xbase.MemoHandler.open_dbf_with_memo("data.dbf", [:read, :write])

# Append record with memo content
{:ok, handler} = Xbase.MemoHandler.append_record_with_memo(handler, %{
  "NAME" => "John Doe",
  "NOTES" => "This is a long memo that will be stored in the DBT file automatically"
})

# Read record with resolved memo content
{:ok, record} = Xbase.MemoHandler.read_record_with_memo(handler, 0)
# => %{"NAME" => "John Doe", "NOTES" => "This is a long memo..."}

# Update memo content
{:ok, handler} = Xbase.MemoHandler.update_record_with_memo(handler, 0, %{
  "NOTES" => "Updated memo content"
})

Xbase.MemoHandler.close_memo_files(handler)

Using Indexes

# Open DBF with index
{:ok, dbf} = Xbase.Parser.open_dbf("data.dbf")
{:ok, cdx} = Xbase.CdxParser.open_cdx("data.cdx")

# Search using index
{:ok, key_info} = Xbase.CdxParser.search_key(cdx, "SMITH")
# => Returns record number for fast access

# Range queries
keys = Xbase.CdxParser.search_range(cdx, "A", "M")

Xbase.CdxParser.close_cdx(cdx)

Transactions

# Wrap operations in a transaction
{:ok, result} = Xbase.Parser.with_transaction(dbf, fn dbf ->
  {:ok, dbf} = Xbase.Parser.append_record(dbf, record1)
  {:ok, dbf} = Xbase.Parser.append_record(dbf, record2)
  {:ok, dbf} = Xbase.Parser.update_record(dbf, 0, updates)
  {:ok, :success}
end)
# Automatically rolls back on error

Batch Operations

# Batch append for performance
records = [record1, record2, record3, ...]
{:ok, dbf} = Xbase.Parser.batch_append_records(dbf, records)

# Batch update
updates = [{0, %{"STATUS" => "ACTIVE"}}, {1, %{"STATUS" => "INACTIVE"}}]
{:ok, dbf} = Xbase.Parser.batch_update_records(dbf, updates)

# Batch delete
{:ok, dbf} = Xbase.Parser.batch_delete(dbf, [5, 10, 15])

📚 Documentation

Complete Guides

Core Modules

Module Purpose
Xbase.Parser Main DBF file operations (create, read, write, update)
Xbase.MemoHandler High-level memo field integration and transactions
Xbase.Types Data structures and type definitions
Xbase.FieldParser Field type parsing and validation
Xbase.FieldEncoder Field type encoding and formatting
Xbase.CdxParser Index file support for fast lookups
Xbase.DbtParser Low-level DBT memo file reading
Xbase.DbtWriter DBT memo file writing and management

⚡ Performance

Benchmarks

Operation Records Time Memory
Stream read 1M records ~2.5s ~50MB
Batch append 100K records ~1.2s ~25MB
Index search 1M records ~0.01s ~10MB
Memo access 10K memos ~0.8s ~15MB

Optimization Tips

🛡️ Error Handling

Xbase follows Elixir conventions with comprehensive error handling:

case Xbase.Parser.open_dbf("data.dbf") do
  {:ok, dbf} ->
    # Work with the file safely
    process_records(dbf)
  {:error, :enoent} ->
    {:error, "File not found: please check the file path"}
  {:error, :invalid_dbf_header} ->
    {:error, "Invalid DBF file format"}
  {:error, reason} ->
    {:error, "Unexpected error: #{inspect(reason)}"}
end

Error Categories

🤝 Contributing

We welcome contributions! Here's how you can help:

Development Setup

git clone https://github.com/your-org/xbase.git
cd xbase
mix deps.get
mix test

Contributing Guidelines

Running Tests

mix test                    # Run all tests
mix test --cover           # Run with coverage
mix dialyzer               # Type checking
mix credo                  # Code quality

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments


Need help? Check out our documentation or open an issue.