lift 0.1.2

Lift migration syntax parser for qraft.
Documentation

lift

lift is a Rust workspace for the Lift migration language.

This repository contains the language parser, a small language server, and a tree-sitter grammar. It is for tools that need to read, validate, or edit Lift migration files. It does not apply migrations to a database.

What Lift looks like

Lift is a small migration DSL with tables, enums, inline SQL, and optional rollback blocks.

create users {
  id
  email text unique
  tenant_id uuid
  remember_token
  timestamps
  soft_deletes
  unique (tenant_id, email)
}

alter users {
  add column timezone text default "UTC"
}

backfill {
  update users
  set timezone = 'UTC'
  where timezone is null;
}

down {
  drop users
}

The language also supports enum migrations:

create enum post_status { draft, published, archived }

alter enum post_status {
  add value deleted
}

Migration files

The parser reads migrations from a directory. It recognizes:

  • Lift files named <id>_<name>.lift
  • SQL files named <id>_<name>.sql
  • optional SQL rollback files named <id>_<name>.down.sql

Examples:

  • 202604210001_create_users.lift
  • 202604220001_enable_pgcrypto.sql
  • 202604220001_enable_pgcrypto.down.sql

Lift migrations may contain one optional down { ... } block. SQL migrations use a separate .down.sql file when a rollback is needed.

When loading a directory, the parser sorts migrations by numeric id and returns them as a single ordered list.

Workspace

This workspace has three crates.

lift

crates/lift parses Lift source and SQL migration files into Rust data structures such as MigrationFile, Statement, TableItem, and Column.

Use it when you need to:

  • read a migration directory
  • validate file names and migration contents
  • inspect schema statements programmatically
  • accept either Lift or SQL migrations

Small example:

use lift::{Statement, default_migration_dir, load_migrations};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let migrations = load_migrations(&default_migration_dir())?;

    for migration in migrations {
        println!("{} {}", migration.id, migration.name);

        for statement in &migration.up {
            match statement {
                Statement::CreateTable { name, .. } => println!("- create {name}"),
                Statement::AlterTable { name, .. } => println!("- alter {name}"),
                Statement::CreateEnum { name, .. } => println!("- create enum {name}"),
                Statement::Sql { .. } => println!("- sql"),
                Statement::Backfill { .. } => println!("- backfill"),
                _ => {}
            }
        }
    }

    Ok(())
}

The crate also exposes parse_migration_file and parse_source when you want to parse one migration at a time.

lift-lsp

crates/lift-lsp is a language server for .lift files. It runs over stdio.

cargo run -p lift-lsp

Today it focuses on a small set of editor features:

  • diagnostics for invalid Lift syntax
  • diagnostics for invalid migration file names
  • keyword completion for Lift syntax

tree-sitter-lift

crates/tree-sitter-lift provides a tree-sitter grammar for Lift, along with queries for highlighting, indentation, and injections.

Rust example:

let mut parser = tree_sitter::Parser::new();
parser.set_language(&tree_sitter_lift::language())?;

Development

Build the workspace:

cargo build --workspace

Run tests:

cargo test --workspace

If you change crates/tree-sitter-lift/grammar.js, regenerate the parser:

tree-sitter generate crates/tree-sitter-lift

License

Licensed under either of:

  • MIT, see LICENSE-MIT
  • Apache-2.0, see LICENSE-APACHE