dbcrossbarlib 0.2.5

Library for copying data between databases (pre-release)
Documentation
//! This file contains a [`rust-peg`][peg] grammar. A "PEG" is a "parser
//! expression grammar". It's basically similar to a regular expression,
//! except it can contain recursive rules. See the site for an overview
//! of the basic syntax.
//!
//! `#quiet` and `#expected` are used in a few places to give better
//! error messages. `#quiet` suppresses certain possible tokens (such as
//! whitespace) from "expected ___" errors, and `#expected` allows us to
//! insert descriptive names into those messages.
//!
//! [peg]: https://github.com/kevinmehall/rust-peg

use super::super::{PgColumn, PgCreateTable, PgDataType, PgScalarDataType};
use crate::schema::Srid;

/// A `CREATE TABLE` expression.
pub create_table -> PgCreateTable
    = ws? "CREATE"i ws ("UNLOGGED"i ws)? "TABLE"i ws name:identifier ws? "("
        ws? columns:(column ** (ws? "," ws?)) ws?
      ")" ws? (";" ws?)?
    {
        PgCreateTable {
            name,
            columns,
            if_not_exists: false,
            // We don't worry about trying to parse this, which we only use
            // internally at the moment.
            temporary: false,
        }
    }

/// A column expression of the form "name type".
column -> PgColumn
    = name:identifier ws data_type:data_type is_nullable:is_nullable primary_key? {
        PgColumn {
            name,
            is_nullable,
            data_type,
        }
    }

/// An optional `NOT NULL` expression.
is_nullable -> bool
    = ws "NOT"i ws "NULL"i { false }
    / { true }

/// A `PRIMARY KEY` specifier. We can ignore this.
primary_key
    = ws "PRIMARY"i ws "KEY"i

/// A Postgres data type.
data_type -> PgDataType
    = #quiet<
        // Array type.
        data_type:scalar_data_type ws? "[" ws? "]" {
            PgDataType::Array { dimension_count: 1, ty: data_type }
        }

        // All other types.
        / data_type:scalar_data_type { PgDataType::Scalar(data_type) }
    >
    / #expected("data type")

/// A scalar data type, never an array.
scalar_data_type -> PgScalarDataType
    = "bigint"i { PgScalarDataType::Bigint }
    / "boolean"i { PgScalarDataType::Boolean }
    / "date"i { PgScalarDataType::Date }
    / "double"i ws "precision"i { PgScalarDataType::DoublePrecision }
    / "float"i { PgScalarDataType::DoublePrecision }
    / "public."i? "geometry"i ws? "(" ws? identifier ws? "," ws? srid:srid ws? ")" {
        PgScalarDataType::Geometry(Srid::new(srid))
    }
    / "integer"i { PgScalarDataType::Int } // Longer keyword first!
    / "int"i { PgScalarDataType::Int }
    / "jsonb"i { PgScalarDataType::Jsonb }
    / "json"i { PgScalarDataType::Json }
    / "numeric"i { PgScalarDataType::Numeric }
    / "real"i { PgScalarDataType::Real }
    / "smallint"i { PgScalarDataType::Smallint }
    / "text"i { PgScalarDataType::Text }
    / "timestamp"i ws "with"i ws "time"i ws "zone"i {
        PgScalarDataType::TimestampWithTimeZone
    }
    / "timestamp"i ws "without"i ws "time"i ws "zone"i {
        PgScalarDataType::TimestampWithoutTimeZone
    }
    / "timestamp"i {
        PgScalarDataType::TimestampWithoutTimeZone
    }
    / "uuid"i { PgScalarDataType::Uuid }

/// A GeoJSON SRID number, used to identify a coordinate system.
srid -> u32
    = srid:$([0-9]+) { srid.parse().expect("should always parse") }

/// An SQL identifier.
identifier -> String
    = #quiet<
        // Unquoted identifier.
        id:$([A-Za-z_][A-Za-z_0-9$]*) { id.to_string() }

        // Double-quoted identifier.
        / "\"" quoted:$(([^"] / "\"\"")*) "\"" {
            quoted.replace("\"\"", "\"").to_string()
        }
    >
    / #expected("identifier")

// One or more characters of whitespace, including comments.
ws = #quiet<
    ([ \t\r\n] / ("--" [^\n]* "\n"))+
>