diff --git a/Cargo.lock b/Cargo.lock index 2a9a24e30..1633f3cbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2766,6 +2766,7 @@ dependencies = [ "pgls_console", "pgls_diagnostics", "pgls_test_utils", + "schemars", "serde", "serde_json", "sqlx", @@ -5608,12 +5609,14 @@ dependencies = [ "pgls_diagnostics", "pgls_env", "pgls_pglinter", + "pgls_schema_cache", "pgls_splinter", "pgls_workspace", "proc-macro2", "pulldown-cmark", "quote", "regex", + "schemars", "xtask", ] diff --git a/crates/pgls_schema_cache/Cargo.toml b/crates/pgls_schema_cache/Cargo.toml index b2745a89d..74d6b4344 100644 --- a/crates/pgls_schema_cache/Cargo.toml +++ b/crates/pgls_schema_cache/Cargo.toml @@ -14,6 +14,8 @@ version = "0.0.0" default = ["db"] # Enable serde serialization/deserialization for all types (needed for WASM) serde = [] +# Enable schemars JsonSchema derive for schema generation/codegen +schema = ["dep:schemars"] # Enable database connectivity (sqlx, async-std, tokio, futures-util) # Disable this feature for WASM builds db = ["dep:sqlx", "dep:async-std", "dep:futures-util", "dep:tokio"] @@ -24,6 +26,7 @@ async-std = { version = "1.12.0", optional = true } futures-util = { version = "0.3.31", optional = true } pgls_console.workspace = true pgls_diagnostics.workspace = true +schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true sqlx = { workspace = true, optional = true } diff --git a/crates/pgls_schema_cache/src/columns.rs b/crates/pgls_schema_cache/src/columns.rs index 3f11cd8ed..941d74e1a 100644 --- a/crates/pgls_schema_cache/src/columns.rs +++ b/crates/pgls_schema_cache/src/columns.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::schema_cache::SchemaCacheItem; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum ColumnClassKind { OrdinaryTable, View, @@ -40,6 +41,7 @@ impl From for ColumnClassKind { } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Column { pub name: String, @@ -69,6 +71,7 @@ pub struct Column { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct ForeignKeyReference { pub schema: Option, pub table: String, diff --git a/crates/pgls_schema_cache/src/extensions.rs b/crates/pgls_schema_cache/src/extensions.rs index b7706fccf..d9ca0c6e6 100644 --- a/crates/pgls_schema_cache/src/extensions.rs +++ b/crates/pgls_schema_cache/src/extensions.rs @@ -6,6 +6,7 @@ use sqlx::PgPool; use crate::schema_cache::SchemaCacheItem; #[derive(Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Extension { pub name: String, pub schema: Option, diff --git a/crates/pgls_schema_cache/src/functions.rs b/crates/pgls_schema_cache/src/functions.rs index 675484aa4..80a65553e 100644 --- a/crates/pgls_schema_cache/src/functions.rs +++ b/crates/pgls_schema_cache/src/functions.rs @@ -7,6 +7,7 @@ use sqlx::PgPool; use crate::schema_cache::SchemaCacheItem; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum ProcKind { #[default] Function, @@ -35,6 +36,7 @@ impl From for ProcKind { /// `Behavior` describes the characteristics of the function. Is it deterministic? Does it changed due to side effects, and if so, when? #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum Behavior { /// The function is a pure function (same input leads to same output.) Immutable, @@ -62,6 +64,7 @@ impl From> for Behavior { } #[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct FunctionArg { /// `in`, `out`, or `inout`. pub mode: String, @@ -75,6 +78,7 @@ pub struct FunctionArg { } #[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct FunctionArgs { pub args: Vec, } @@ -88,6 +92,7 @@ impl From> for FunctionArgs { } #[derive(Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Function { /// The Id (`oid`). pub id: i64, diff --git a/crates/pgls_schema_cache/src/indexes.rs b/crates/pgls_schema_cache/src/indexes.rs index 54223367a..b55bc0cba 100644 --- a/crates/pgls_schema_cache/src/indexes.rs +++ b/crates/pgls_schema_cache/src/indexes.rs @@ -7,6 +7,7 @@ use crate::schema_cache::SchemaCacheItem; use serde::{Deserialize, Serialize}; #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Index { pub id: i64, pub schema: String, diff --git a/crates/pgls_schema_cache/src/policies.rs b/crates/pgls_schema_cache/src/policies.rs index 760c24fce..88a131568 100644 --- a/crates/pgls_schema_cache/src/policies.rs +++ b/crates/pgls_schema_cache/src/policies.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::schema_cache::SchemaCacheItem; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum PolicyCommand { Select, Insert, @@ -58,6 +59,7 @@ impl From for Policy { } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Policy { pub name: String, pub table_name: String, diff --git a/crates/pgls_schema_cache/src/roles.rs b/crates/pgls_schema_cache/src/roles.rs index a11074c0c..6cad5922f 100644 --- a/crates/pgls_schema_cache/src/roles.rs +++ b/crates/pgls_schema_cache/src/roles.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::schema_cache::SchemaCacheItem; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Role { pub name: String, pub is_super_user: bool, diff --git a/crates/pgls_schema_cache/src/schema_cache.rs b/crates/pgls_schema_cache/src/schema_cache.rs index 5fc06500a..5f72460a3 100644 --- a/crates/pgls_schema_cache/src/schema_cache.rs +++ b/crates/pgls_schema_cache/src/schema_cache.rs @@ -14,6 +14,7 @@ use crate::versions::Version; use crate::{Extension, Role, Trigger}; #[derive(Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(default)] pub struct SchemaCache { pub schemas: Vec, diff --git a/crates/pgls_schema_cache/src/schemas.rs b/crates/pgls_schema_cache/src/schemas.rs index 9de178f59..20d81ee62 100644 --- a/crates/pgls_schema_cache/src/schemas.rs +++ b/crates/pgls_schema_cache/src/schemas.rs @@ -6,6 +6,7 @@ use sqlx::PgPool; use crate::schema_cache::SchemaCacheItem; #[derive(Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Schema { pub id: i64, pub name: String, diff --git a/crates/pgls_schema_cache/src/sequences.rs b/crates/pgls_schema_cache/src/sequences.rs index 39503946e..f317872df 100644 --- a/crates/pgls_schema_cache/src/sequences.rs +++ b/crates/pgls_schema_cache/src/sequences.rs @@ -7,6 +7,7 @@ use crate::schema_cache::SchemaCacheItem; use serde::{Deserialize, Serialize}; #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Sequence { pub id: i64, pub schema: String, diff --git a/crates/pgls_schema_cache/src/tables.rs b/crates/pgls_schema_cache/src/tables.rs index f21aebcb2..1423526ae 100644 --- a/crates/pgls_schema_cache/src/tables.rs +++ b/crates/pgls_schema_cache/src/tables.rs @@ -6,6 +6,7 @@ use sqlx::PgPool; use crate::schema_cache::SchemaCacheItem; #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum ReplicaIdentity { #[default] Default, @@ -27,6 +28,7 @@ impl From for ReplicaIdentity { } #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum TableKind { #[default] Ordinary, @@ -55,6 +57,7 @@ impl From for TableKind { } #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Table { pub id: i64, pub schema: String, diff --git a/crates/pgls_schema_cache/src/triggers.rs b/crates/pgls_schema_cache/src/triggers.rs index 8fd019df9..4f5df73e5 100644 --- a/crates/pgls_schema_cache/src/triggers.rs +++ b/crates/pgls_schema_cache/src/triggers.rs @@ -5,6 +5,7 @@ use crate::schema_cache::SchemaCacheItem; use strum::{EnumIter, IntoEnumIterator}; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum TriggerAffected { Row, Statement, @@ -22,6 +23,7 @@ impl From for TriggerAffected { } #[derive(Debug, PartialEq, Eq, EnumIter, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum TriggerEvent { Insert, Delete, @@ -51,6 +53,7 @@ impl From for TriggerEvents { } #[derive(Debug, PartialEq, Eq, EnumIter, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum TriggerTiming { Before, After, @@ -92,6 +95,7 @@ pub struct TriggerQueried { } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Trigger { pub name: String, pub table_name: String, diff --git a/crates/pgls_schema_cache/src/types.rs b/crates/pgls_schema_cache/src/types.rs index ec0ae9be2..8014eeda5 100644 --- a/crates/pgls_schema_cache/src/types.rs +++ b/crates/pgls_schema_cache/src/types.rs @@ -7,11 +7,13 @@ use sqlx::PgPool; use crate::schema_cache::SchemaCacheItem; #[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct TypeAttributes { pub attrs: Vec, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct PostgresTypeAttribute { pub name: String, pub type_id: i64, @@ -26,6 +28,7 @@ impl From> for TypeAttributes { } #[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Enums { pub values: Vec, } @@ -39,6 +42,7 @@ impl From> for Enums { } #[derive(Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct PostgresType { pub id: i64, pub name: String, diff --git a/crates/pgls_schema_cache/src/versions.rs b/crates/pgls_schema_cache/src/versions.rs index f04b28f3d..1a6962f3d 100644 --- a/crates/pgls_schema_cache/src/versions.rs +++ b/crates/pgls_schema_cache/src/versions.rs @@ -6,6 +6,7 @@ use sqlx::PgPool; use crate::schema_cache::SchemaCacheItem; #[derive(Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Version { pub version: Option, pub version_num: Option, diff --git a/justfile b/justfile index 07aff5fb8..fe1886086 100644 --- a/justfile +++ b/justfile @@ -28,6 +28,7 @@ gen-lint: cargo run -p xtask_codegen -- analyser cargo run -p xtask_codegen -- configuration cargo run -p xtask_codegen -- bindings + cargo run -p xtask_codegen -- schema-types cargo run -p xtask_codegen -- splinter cargo run -p xtask_codegen -- pglinter cargo run -p rules_check @@ -113,6 +114,7 @@ ready: cargo run -p xtask_codegen -- configuration cargo run -p docs_codegen cargo run -p xtask_codegen -- bindings + cargo run -p xtask_codegen -- schema-types cargo sqlx prepare --workspace just format # format after codegen, so we don't have staged changes before lint-fix just lint-fix diff --git a/packages/@postgres-language-server/wasm/README.md b/packages/@postgres-language-server/wasm/README.md index 103306b32..62ecee627 100644 --- a/packages/@postgres-language-server/wasm/README.md +++ b/packages/@postgres-language-server/wasm/README.md @@ -47,12 +47,15 @@ const hover = workspace.hover("/query.sql", 14); // position over "users" ### With Schema -For schema-aware completions and hover, provide your database schema: +For schema-aware completions and hover, provide your database schema. +TypeScript types exported as `SchemaCache`: ```typescript +import type { SchemaCache } from '@postgres-language-server/wasm'; + const workspace = await createWorkspace(); -// Set schema (matches pgls_schema_cache format) +// Set schema workspace.setSchema(JSON.stringify({ schemas: [{ id: 1, name: 'public', owner: 'postgres', ... }], tables: [{ id: 1, schema: 'public', name: 'users', ... }], @@ -60,7 +63,7 @@ workspace.setSchema(JSON.stringify({ functions: [], types: [], // ... -})); +} satisfies SchemaCache)); workspace.insertFile('/query.sql', 'SELECT * FROM '); const completions = workspace.complete('/query.sql', 14); diff --git a/packages/@postgres-language-server/wasm/package.json b/packages/@postgres-language-server/wasm/package.json index bcae8f1b4..ce7e7a8a6 100644 --- a/packages/@postgres-language-server/wasm/package.json +++ b/packages/@postgres-language-server/wasm/package.json @@ -46,8 +46,9 @@ "scripts": { "build": "bun run build:wasm && bun run build:ts", "build:wasm": "../../../crates/pgls_wasm/build-wasm.sh --release && mkdir -p wasm && cp ../../../crates/pgls_wasm/dist/pgls.js ../../../crates/pgls_wasm/dist/pgls.wasm ./wasm/", - "build:ts": "tsc -p tsconfig.build.json && cp wasm/pgls.wasm wasm/pgls.js dist/", - "build:types": "tsc --declaration --emitDeclarationOnly --outDir dist", + "gen:schema-types": "cargo run -p xtask_codegen -- schema-types", + "build:ts": "bun run gen:schema-types && tsc -p tsconfig.build.json && cp src/schema-cache.d.ts dist/schema-cache.d.ts && cp wasm/pgls.wasm wasm/pgls.js dist/", + "build:types": "bun run gen:schema-types && tsc --declaration --emitDeclarationOnly --outDir dist && cp src/schema-cache.d.ts dist/schema-cache.d.ts", "test": "bun test", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", diff --git a/packages/@postgres-language-server/wasm/src/schema-cache.d.ts b/packages/@postgres-language-server/wasm/src/schema-cache.d.ts new file mode 100644 index 000000000..5a363bce1 --- /dev/null +++ b/packages/@postgres-language-server/wasm/src/schema-cache.d.ts @@ -0,0 +1,239 @@ +// Generated file, do not edit by hand, see `xtask/codegen` +export interface SchemaCache { + columns?: Column[]; + extensions?: Extension[]; + functions?: Function[]; + indexes?: Index[]; + policies?: Policy[]; + roles?: Role[]; + schemas?: Schema[]; + sequences?: Sequence[]; + tables?: Table[]; + triggers?: Trigger[]; + types?: PostgresType[]; + version?: Version; +} +export interface Column { + /** + * What type of class does this column belong to? + */ + class_kind: ColumnClassKind; + /** + * Comment inserted via `COMMENT ON COLUMN my_table.my_comment '...'`, if present. + */ + comment?: string; + /** + * The Default "value" of the column. Might be a function call, hence "_expr". + */ + default_expr?: string; + is_nullable: boolean; + is_primary_key: boolean; + is_unique: boolean; + name: string; + /** + * the column number in the table + */ + number: number; + schema_name: string; + table_name: string; + table_oid: number; + type_id: number; + type_name?: string; + varchar_length?: number; +} +export interface Extension { + comment?: string; + default_version: string; + installed_version?: string; + name: string; + schema?: string; +} +export interface Function { + /** + * The Rust representation of the function's arguments. + */ + args: FunctionArgs; + /** + * Comma-separated list of argument types, in the form required for a CREATE FUNCTION statement. For example, `"text, smallint"`. `None` if the function doesn't take any arguments. + */ + argument_types?: string; + /** + * See `Behavior`. + */ + behavior: Behavior; + /** + * The body of the function – the `declare [..] begin [..] end [..]` block.` Not set for internal functions. + */ + body?: string; + /** + * The full definition of the function. Includes the full `CREATE OR REPLACE...` shenanigans. Not set for internal functions. + */ + definition?: string; + /** + * The Id (`oid`). + */ + id: number; + /** + * Comma-separated list of argument types, in the form required to identify a function in an ALTER FUNCTION statement. For example, `"text, smallint"`. `None` if the function doesn't take any arguments. + */ + identity_argument_types?: string; + /** + * Does the function returns multiple values of a data type? + */ + is_set_returning_function: boolean; + kind: ProcKind; + /** + * e.g. `plpgsql/sql` or `internal`. + */ + language: string; + /** + * The name of the function. + */ + name: string; + /** + * The return type, for example "text", "trigger", or "void". + */ + return_type?: string; + /** + * An ID identifying the return type. For example, `2275` refers to `cstring`. 2278 refers to `void`. + */ + return_type_id?: number; + /** + * If the return type is a composite type, this will point the matching entry's `oid` column in the `pg_class` table. `None` if the function does not return a composite type. + */ + return_type_relation_id?: number; + /** + * The name of the schema the function belongs to. + */ + schema: string; + /** + * Is the function's security set to `Definer` (true) or `Invoker` (false)? + */ + security_definer: boolean; +} +export interface Index { + id: number; + name: string; + schema: string; + table_name: string; +} +export interface Policy { + command: PolicyCommand; + is_permissive: boolean; + name: string; + role_names: string[]; + schema_name: string; + security_qualification?: string; + table_name: string; + with_check?: string; +} +export interface Role { + can_bypass_rls: boolean; + can_create_db: boolean; + can_create_roles: boolean; + can_login: boolean; + comment?: string; + has_member: string[]; + is_super_user: boolean; + member_of: string[]; + name: string; +} +export interface Schema { + allowed_creators: string[]; + allowed_users: string[]; + comment?: string; + function_count: number; + id: number; + name: string; + owner: string; + table_count: number; + total_size: string; + view_count: number; +} +export interface Sequence { + id: number; + name: string; + schema: string; +} +export interface Table { + bytes: number; + comment?: string; + dead_rows_estimate: number; + id: number; + live_rows_estimate: number; + name: string; + replica_identity: ReplicaIdentity; + rls_enabled: boolean; + rls_forced: boolean; + schema: string; + size: string; + table_kind: TableKind; +} +export interface Trigger { + affected: TriggerAffected; + events: TriggerEvent[]; + name: string; + proc_name: string; + proc_schema: string; + table_name: string; + table_schema: string; + timing: TriggerTiming; +} +export interface PostgresType { + attributes: TypeAttributes; + comment?: string; + enums: Enums; + format: string; + id: number; + name: string; + schema: string; +} +export interface Version { + active_connections?: number; + major_version?: number; + max_connections?: number; + version?: string; + version_num?: number; +} +export type ColumnClassKind = + | "OrdinaryTable" + | "View" + | "MaterializedView" + | "ForeignTable" + | "PartitionedTable"; +export interface FunctionArgs { + args: FunctionArg[]; +} +/** + * `Behavior` describes the characteristics of the function. Is it deterministic? Does it changed due to side effects, and if so, when? + */ +export type Behavior = "Immutable" | "Stable" | "Volatile"; +export type ProcKind = "Function" | "Aggregate" | "Window" | "Procedure"; +export type PolicyCommand = "Select" | "Insert" | "Update" | "Delete" | "All"; +export type ReplicaIdentity = "Default" | "Index" | "Full" | "Nothing"; +export type TableKind = "Ordinary" | "View" | "MaterializedView" | "Partitioned"; +export type TriggerAffected = "Row" | "Statement"; +export type TriggerEvent = "Insert" | "Delete" | "Update" | "Truncate"; +export type TriggerTiming = "Before" | "After" | "Instead"; +export interface TypeAttributes { + attrs: PostgresTypeAttribute[]; +} +export interface Enums { + values: string[]; +} +export interface FunctionArg { + has_default?: boolean; + /** + * `in`, `out`, or `inout`. + */ + mode: string; + name: string; + /** + * Refers to the argument type's ID in the `pg_type` table. + */ + type_id: number; +} +export interface PostgresTypeAttribute { + name: string; + type_id: number; +} diff --git a/packages/@postgres-language-server/wasm/src/types.ts b/packages/@postgres-language-server/wasm/src/types.ts index 411041e8a..0b098f036 100644 --- a/packages/@postgres-language-server/wasm/src/types.ts +++ b/packages/@postgres-language-server/wasm/src/types.ts @@ -2,6 +2,8 @@ * TypeScript type definitions for the Postgres Language Server WASM API. */ +import type { SchemaCache } from "./schema-cache"; + /** * A diagnostic message from the linter. */ @@ -35,47 +37,9 @@ export interface CompletionItem { } /** - * Schema cache representing database metadata. - * This structure mirrors the Rust SchemaCache type. + * Schema cache type generated from Rust `SchemaCache` via schemars. */ -export interface SchemaCache { - schemas: Schema[]; - tables: Table[]; - functions: Function[]; - types: Type[]; - // Add more as needed based on pgls_schema_cache -} - -export interface Schema { - name: string; - owner?: string; -} - -export interface Table { - name: string; - schema: string; - columns: Column[]; -} - -export interface Column { - name: string; - dataType: string; - isNullable: boolean; - defaultValue?: string; -} - -export interface Function { - name: string; - schema: string; - returnType: string; - arguments: string[]; -} - -export interface Type { - name: string; - schema: string; - kind: string; -} +export type { SchemaCache } from "./schema-cache"; /** * Options for initializing the workspace. diff --git a/xtask/codegen/Cargo.toml b/xtask/codegen/Cargo.toml index cee4fbe64..34d991e55 100644 --- a/xtask/codegen/Cargo.toml +++ b/xtask/codegen/Cargo.toml @@ -5,18 +5,20 @@ publish = false version = "0.0.0" [dependencies] -anyhow = { workspace = true } -bpaf = { workspace = true, features = ["derive"] } -convert_case = { workspace = true } -pgls_analyse = { workspace = true } -pgls_analyser = { workspace = true } -pgls_diagnostics = { workspace = true } -pgls_env = { workspace = true } -pgls_pglinter = { workspace = true } -pgls_splinter = { workspace = true } -pgls_workspace = { workspace = true, features = ["schema"] } -proc-macro2 = { workspace = true, features = ["span-locations"] } -pulldown-cmark = { version = "0.12.2" } -quote = "1.0.36" -regex = "1.11" -xtask = { path = '../', version = "0.0" } +anyhow = { workspace = true } +bpaf = { workspace = true, features = ["derive"] } +convert_case = { workspace = true } +pgls_analyse = { workspace = true } +pgls_analyser = { workspace = true } +pgls_diagnostics = { workspace = true } +pgls_env = { workspace = true } +pgls_pglinter = { workspace = true } +pgls_schema_cache = { workspace = true, features = ["schema"] } +pgls_splinter = { workspace = true } +pgls_workspace = { workspace = true, features = ["schema"] } +proc-macro2 = { workspace = true, features = ["span-locations"] } +pulldown-cmark = { version = "0.12.2" } +quote = "1.0.36" +regex = "1.11" +schemars = { workspace = true } +xtask = { path = '../', version = "0.0" } diff --git a/xtask/codegen/src/generate_bindings.rs b/xtask/codegen/src/generate_bindings.rs index 0ad4937bb..52547eb9b 100644 --- a/xtask/codegen/src/generate_bindings.rs +++ b/xtask/codegen/src/generate_bindings.rs @@ -4,7 +4,7 @@ use convert_case::{Case, Casing}; use pgls_workspace::workspace_types::{generate_type, methods, ModuleQueue}; use xtask::{project_root, Mode, Result}; -pub fn generate_bindings(mode: Mode) -> Result<()> { +pub fn generate_bindings(mode: &Mode) -> Result<()> { let bindings_path_postgrestools = project_root().join("packages/@postgrestools/backend-jsonrpc/src/workspace.ts"); let bindings_path_postgres_language_server = @@ -22,26 +22,20 @@ pub fn generate_bindings(mode: Mode) -> Result<()> { let camel_case = method.name.to_case(Case::Camel); - // Workspace interface method signature workspace_members.push(format!( "\t{camel_case}(params: {params}): Promise<{result}>;" )); - // createWorkspace implementation method impl_members.push(format!( "\t\t{camel_case}(params) {{\n\t\t\treturn transport.request(\"pgls/{}\", params);\n\t\t}}", method.name )); } - // Build the full output let mut output = String::new(); - - // Header output.push_str("// Generated file, do not edit by hand, see `xtask/codegen`\n"); output.push_str("import type { Transport } from \"./transport\";\n"); - // Type declarations for (decl, description) in &declarations { if let Some(description) = description { output.push_str(&format!("/**\n * {description}\n */\n")); @@ -49,10 +43,8 @@ pub fn generate_bindings(mode: Mode) -> Result<()> { output.push_str(&format!("export {decl}\n")); } - // Configuration type alias output.push_str("export type Configuration = PartialConfiguration;\n"); - // Workspace interface workspace_members.push("\tdestroy(): void;".to_string()); output.push_str("export interface Workspace {\n"); for member in &workspace_members { @@ -61,7 +53,6 @@ pub fn generate_bindings(mode: Mode) -> Result<()> { } output.push_str("}\n"); - // createWorkspace function impl_members.push("\t\tdestroy() {\n\t\t\ttransport.destroy();\n\t\t}".to_string()); output.push_str("export function createWorkspace(transport: Transport): Workspace {\n"); output.push_str("\treturn {\n"); @@ -70,9 +61,8 @@ pub fn generate_bindings(mode: Mode) -> Result<()> { output.push_str("\t};\n"); output.push_str("}\n"); - // Generate for both packages (dual publishing) - update(&bindings_path_postgrestools, &output, &mode)?; - update(&bindings_path_postgres_language_server, &output, &mode)?; + update(&bindings_path_postgrestools, &output, mode)?; + update(&bindings_path_postgres_language_server, &output, mode)?; Ok(()) } diff --git a/xtask/codegen/src/generate_schema_types.rs b/xtask/codegen/src/generate_schema_types.rs new file mode 100644 index 000000000..7be519fbf --- /dev/null +++ b/xtask/codegen/src/generate_schema_types.rs @@ -0,0 +1,30 @@ +use crate::update; +use pgls_schema_cache::SchemaCache; +use pgls_workspace::workspace_types::{generate_type, ModuleQueue}; +use schemars::r#gen::{SchemaGenerator, SchemaSettings}; +use xtask::{project_root, Mode, Result}; + +pub fn generate_schema_types(mode: Mode) -> Result<()> { + let output_path = + project_root().join("packages/@postgres-language-server/wasm/src/schema-cache.d.ts"); + + let mut declarations: Vec<(String, Option<&String>)> = Vec::new(); + let mut queue = ModuleQueue::default(); + let schema = SchemaGenerator::from(SchemaSettings::openapi3()).root_schema_for::(); + + generate_type(&mut declarations, &mut queue, &schema); + + let mut output = String::new(); + output.push_str("// Generated file, do not edit by hand, see `xtask/codegen`\n"); + + for (decl, description) in &declarations { + if let Some(description) = description { + output.push_str(&format!("/**\n * {description}\n */\n")); + } + output.push_str(&format!("export {decl}\n")); + } + + update(&output_path, &output, &mode)?; + + Ok(()) +} diff --git a/xtask/codegen/src/lib.rs b/xtask/codegen/src/lib.rs index cdfc2d276..20ff690e2 100644 --- a/xtask/codegen/src/lib.rs +++ b/xtask/codegen/src/lib.rs @@ -8,6 +8,7 @@ mod generate_configuration; mod generate_crate; mod generate_new_analyser_rule; mod generate_pglinter; +mod generate_schema_types; mod generate_splinter; pub use self::generate_analyser::generate_analyser; @@ -16,6 +17,7 @@ pub use self::generate_configuration::{generate_rules_configuration, generate_to pub use self::generate_crate::generate_crate; pub use self::generate_new_analyser_rule::generate_new_analyser_rule; pub use self::generate_pglinter::generate_pglinter; +pub use self::generate_schema_types::generate_schema_types; pub use self::generate_splinter::generate_splinter; use bpaf::Bpaf; use generate_new_analyser_rule::Category; @@ -117,4 +119,7 @@ pub enum TaskCommand { /// Generate pglinter rules from pglinter_repo/sql/rules.sql #[bpaf(command)] Pglinter, + /// Generate schema cache TypeScript types + #[bpaf(command)] + SchemaTypes, } diff --git a/xtask/codegen/src/main.rs b/xtask/codegen/src/main.rs index ca425db00..beb20702e 100644 --- a/xtask/codegen/src/main.rs +++ b/xtask/codegen/src/main.rs @@ -3,7 +3,8 @@ use xtask::{project_root, pushd, Result}; use xtask_codegen::{ generate_analyser, generate_bindings, generate_crate, generate_new_analyser_rule, - generate_pglinter, generate_rules_configuration, generate_splinter, task_command, TaskCommand, + generate_pglinter, generate_rules_configuration, generate_schema_types, generate_splinter, + task_command, TaskCommand, }; fn main() -> Result<()> { @@ -29,7 +30,7 @@ fn main() -> Result<()> { generate_rules_configuration(Overwrite)?; } TaskCommand::Bindings => { - generate_bindings(Overwrite)?; + generate_bindings(&Overwrite)?; } TaskCommand::Splinter => { generate_splinter()?; @@ -37,6 +38,9 @@ fn main() -> Result<()> { TaskCommand::Pglinter => { generate_pglinter()?; } + TaskCommand::SchemaTypes => { + generate_schema_types(Overwrite)?; + } } Ok(())