Skip to content

refactor: migrate to jsonc config #239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Mar 17, 2025
5 changes: 1 addition & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/pglt_analyse/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"], optional = true }

[features]
serde = ["dep:serde", "dep:schemars", "dep:biome_deserialize", "dep:biome_deserialize_macros"]
schema = ["dep:schemars"]
serde = ["dep:serde", "dep:biome_deserialize", "dep:biome_deserialize_macros"]
26 changes: 9 additions & 17 deletions crates/pglt_analyse/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ use enumflags2::{BitFlags, bitflags};
use std::borrow::Cow;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum RuleCategory {
/// This rule performs static analysis of the source code to detect
/// invalid or error-prone patterns, and emits diagnostics along with
Expand All @@ -26,10 +24,8 @@ pub const SUPPRESSION_ACTION_CATEGORY: &str = "quickfix.suppressRule";
///
/// [CodeActionKind]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeActionKind
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum ActionCategory {
/// Base kind for quickfix actions: 'quickfix'.
///
Expand Down Expand Up @@ -110,10 +106,8 @@ impl ActionCategory {
///
/// [Check the LSP spec](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeActionKind) for more information:
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum RefactorKind {
/// This action describes a refactor with no particular sub-category
None,
Expand Down Expand Up @@ -150,10 +144,8 @@ pub enum RefactorKind {

/// The sub-category of a source code action
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum SourceActionKind {
/// This action describes a source action with no particular sub-category
None,
Expand Down Expand Up @@ -282,7 +274,7 @@ impl<'de> serde::Deserialize<'de> for RuleCategories {
}
}

#[cfg(feature = "serde")]
#[cfg(feature = "schema")]
impl schemars::JsonSchema for RuleCategories {
fn schema_name() -> String {
String::from("RuleCategories")
Expand Down
2 changes: 1 addition & 1 deletion crates/pglt_analyse/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl AnalyserRules {
/// A set of information useful to the analyser infrastructure
#[derive(Debug, Default)]
pub struct AnalyserOptions {
/// A data structured derived from the [`pglt.toml`] file
/// A data structured derived from the [`pglt.jsonc`] file
pub rules: AnalyserRules,
}

Expand Down
10 changes: 7 additions & 3 deletions crates/pglt_analyse/src/rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ use std::fmt::Debug;
use crate::{categories::RuleCategory, context::RuleContext, registry::RegistryVisitor};

#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize),
serde(rename_all = "camelCase")
)]
/// Static metadata containing information about a rule
pub struct RuleMetadata {
/// It marks if a rule is deprecated, and if so a reason has to be provided.
Expand Down Expand Up @@ -271,8 +275,8 @@ impl RuleDiagnostic {
}

#[derive(Debug, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, schemars::JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum RuleSource {
/// Rules from [Squawk](https://squawkhq.com)
Squawk(&'static str),
Expand Down
33 changes: 21 additions & 12 deletions crates/pglt_analyser/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,24 @@ Let's assume that the rule we implement support the following options:
- `threshold`: an integer between 0 and 255;
- `behaviorExceptions`: an array of strings.

We would like to set the options in the `pglt.toml` configuration file:

```toml
[linter.rules.safety.myRule]
level = "warn"
options = {
behavior = "A"
threshold = 20
behaviorExceptions = ["one", "two"]
We would like to set the options in the `pglt.jsonc` configuration file:

```json
{
"linter": {
"rules": {
"safety": {
"myRule": {
"level": "warn",
"options": {
"behavior": "A",
"threshold": 20,
"behaviorExceptions": ["one", "two"]
}
}
}
}
}
}
```

Expand Down Expand Up @@ -132,16 +141,16 @@ We currently require implementing _serde_'s traits `Deserialize`/`Serialize`.

Also, we use other `serde` macros to adjust the JSON configuration:

- `rename_all = "snake_case"`: it renames all fields in camel-case, so they are in line with the naming style of the `pglt.toml`.
- `rename_all = "camelCase"`: it renames all fields in camel-case, so they are in line with the naming style of the `pglt.jsonc`.
- `deny_unknown_fields`: it raises an error if the configuration contains extraneous fields.
- `default`: it uses the `Default` value when the field is missing from `pglt.toml`. This macro makes the field optional.
- `default`: it uses the `Default` value when the field is missing from `pglt.jsonc`. This macro makes the field optional.

You can simply use a derive macros:

```rust
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[serde(rename_all = "snake_case", deny_unknown_fields, default)]
#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
pub struct MyRuleOptions {
#[serde(default, skip_serializing_if = "is_default")]
main_behavior: Behavior,
Expand Down
2 changes: 0 additions & 2 deletions crates/pglt_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ tikv-jemallocator = "0.6.0"
[lib]
doctest = false

[features]

[[bin]]
name = "pglt"
path = "src/main.rs"
2 changes: 1 addition & 1 deletion crates/pglt_cli/src/cli_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub struct CliOptions {
#[bpaf(long("verbose"), switch, fallback(false))]
pub verbose: bool,

/// Set the file path to the configuration file, or the directory path to find `pglt.toml`.
/// Set the file path to the configuration file, or the directory path to find `pglt.jsonc`.
/// If used, it disables the default configuration file resolution.
#[bpaf(long("config-path"), argument("PATH"), optional)]
pub config_path: Option<String>,
Expand Down
5 changes: 3 additions & 2 deletions crates/pglt_cli/src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use pglt_workspace::configuration::create_config;

pub(crate) fn init(mut session: CliSession) -> Result<(), CliDiagnostic> {
let fs = &mut session.app.fs;
create_config(fs, PartialConfiguration::init())?;
let file_created = ConfigName::pglt_toml();
let config = &mut PartialConfiguration::init();
create_config(fs, config)?;
let file_created = ConfigName::pglt_jsonc();
session.app.console.log(markup! {
"
Welcome to the Postgres Language Tools! Let's get you started...
Expand Down
8 changes: 4 additions & 4 deletions crates/pglt_cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub enum PgltCommand {
changed: bool,

/// Use this to specify the base branch to compare against when you're using the --changed
/// flag and the `defaultBranch` is not set in your `pglt.toml`
/// flag and the `defaultBranch` is not set in your `pglt.jsonc`
#[bpaf(long("since"), argument("REF"))]
since: Option<String>,

Expand Down Expand Up @@ -91,7 +91,7 @@ pub enum PgltCommand {
)]
log_path: PathBuf,
/// Allows to set a custom file path to the configuration file,
/// or a custom directory path to find `pglt.toml`
/// or a custom directory path to find `pglt.jsonc`
#[bpaf(env("PGLT_LOG_PREFIX_NAME"), long("config-path"), argument("PATH"))]
config_path: Option<PathBuf>,
},
Expand Down Expand Up @@ -127,7 +127,7 @@ pub enum PgltCommand {
)]
log_path: PathBuf,
/// Allows to set a custom file path to the configuration file,
/// or a custom directory path to find `pglt.toml`
/// or a custom directory path to find `pglt.jsonc`
#[bpaf(env("PGLT_CONFIG_PATH"), long("config-path"), argument("PATH"))]
config_path: Option<PathBuf>,
/// Bogus argument to make the command work with vscode-languageclient
Expand Down Expand Up @@ -164,7 +164,7 @@ pub enum PgltCommand {
#[bpaf(long("stop-on-disconnect"), hide_usage)]
stop_on_disconnect: bool,
/// Allows to set a custom file path to the configuration file,
/// or a custom directory path to find `pglt.toml`
/// or a custom directory path to find `pglt.jsonc`
#[bpaf(env("PGLT_CONFIG_PATH"), long("config-path"), argument("PATH"))]
config_path: Option<PathBuf>,
},
Expand Down
4 changes: 2 additions & 2 deletions crates/pglt_cli/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub enum CliDiagnostic {
IoError(IoDiagnostic),
/// The daemon is not running
ServerNotRunning(ServerNotRunning),
/// The end configuration (`pglt.toml` + other options) is incompatible with the command
/// The end configuration (`pglt.jsonc` + other options) is incompatible with the command
IncompatibleEndConfiguration(IncompatibleEndConfiguration),
/// No files processed during the file system traversal
NoFilesWereProcessed(NoFilesWereProcessed),
Expand Down Expand Up @@ -410,7 +410,7 @@ impl CliDiagnostic {
Self::ServerNotRunning(ServerNotRunning)
}

/// Emitted when the end configuration (`pglt.toml` file + CLI arguments + LSP configuration)
/// Emitted when the end configuration (`pglt.jsonc` file + CLI arguments + LSP configuration)
/// results in a combination of options that doesn't allow to run the command correctly.
///
/// A reason needs to be provided
Expand Down
2 changes: 0 additions & 2 deletions crates/pglt_commands/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,3 @@ sqlx.workspace = true

[lib]
doctest = false

[features]
2 changes: 0 additions & 2 deletions crates/pglt_completions/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,3 @@ pglt_test_utils.workspace = true

[lib]
doctest = false

[features]
1 change: 0 additions & 1 deletion crates/pglt_configuration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ rustc-hash = { workspace = true }
schemars = { workspace = true, features = ["indexmap1"], optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["raw_value"] }
toml = { workspace = true }

[lib]
doctest = false
Expand Down
2 changes: 1 addition & 1 deletion crates/pglt_configuration/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Eq, Partial, PartialEq, Serialize)]
#[partial(derive(Bpaf, Clone, Eq, PartialEq, Merge))]
#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))]
#[partial(serde(rename_all = "snake_case", default, deny_unknown_fields))]
#[partial(serde(rename_all = "camelCase", default, deny_unknown_fields))]
pub struct DatabaseConfiguration {
/// The host of the database.
#[partial(bpaf(long("host")))]
Expand Down
4 changes: 2 additions & 2 deletions crates/pglt_configuration/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ pub enum ConfigurationDiagnostic {
}

impl ConfigurationDiagnostic {
pub fn new_deserialization_error(error: toml::de::Error) -> Self {
pub fn new_deserialization_error(error: serde_json::Error) -> Self {
Self::DeserializationError(DeserializationError {
message: error.message().to_string(),
message: error.to_string(),
})
}

Expand Down
2 changes: 1 addition & 1 deletion crates/pglt_configuration/src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub const DEFAULT_FILE_SIZE_LIMIT: NonZeroU64 =
#[derive(Clone, Debug, Deserialize, Eq, Partial, PartialEq, Serialize)]
#[partial(derive(Bpaf, Clone, Eq, PartialEq, Merge))]
#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))]
#[partial(serde(rename_all = "snake_case", default, deny_unknown_fields))]
#[partial(serde(rename_all = "camelCase", default, deny_unknown_fields))]
pub struct FilesConfiguration {
/// The maximum allowed size for source code files in bytes. Files above
/// this limit will be ignored for performance reasons. Defaults to 1 MiB
Expand Down
13 changes: 11 additions & 2 deletions crates/pglt_configuration/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! This module contains the configuration of `pglt.toml`
//! This module contains the configuration of `pglt.jsonc`
//!
//! The configuration is divided by "tool", and then it's possible to further customise it
//! by language. The language might further options divided by tool.
Expand Down Expand Up @@ -43,8 +43,13 @@ pub const VERSION: &str = match option_env!("PGLT_VERSION") {
#[derive(Clone, Debug, Default, Deserialize, Eq, Partial, PartialEq, Serialize)]
#[partial(derive(Bpaf, Clone, Eq, PartialEq, Merge))]
#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))]
#[partial(serde(deny_unknown_fields, rename_all = "snake_case"))]
#[partial(serde(deny_unknown_fields, rename_all = "camelCase"))]
pub struct Configuration {
/// A field for the [JSON schema](https://json-schema.org/) specification
#[partial(serde(rename = "$schema"))]
#[partial(bpaf(hide))]
pub schema: String,

/// The configuration of the VCS integration
#[partial(type, bpaf(external(partial_vcs_configuration), optional, hide_usage))]
pub vcs: VcsConfiguration,
Expand Down Expand Up @@ -79,6 +84,10 @@ impl PartialConfiguration {
/// Returns the initial configuration.
pub fn init() -> Self {
Self {
// TODO: Update this once we have a static url
schema: Some(format!(
"https://supabase-community.github.io/postgres_lsp/schemas/{VERSION}/schema.json"
)),
files: Some(PartialFilesConfiguration {
ignore: Some(Default::default()),
..Default::default()
Expand Down
2 changes: 1 addition & 1 deletion crates/pglt_configuration/src/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
/// The configuration of the filesystem
#[derive(Clone, Debug, Deserialize, Eq, Partial, PartialEq, Serialize, Default)]
#[partial(derive(Bpaf, Clone, Eq, PartialEq, Merge))]
#[partial(serde(rename_all = "snake_case", default, deny_unknown_fields))]
#[partial(serde(rename_all = "camelCase", default, deny_unknown_fields))]
#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))]
pub struct MigrationsConfiguration {
/// The directory where the migration files are stored
Expand Down
6 changes: 3 additions & 3 deletions crates/pglt_configuration/src/vcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const GIT_IGNORE_FILE_NAME: &str = ".gitignore";
#[partial(derive(Bpaf, Clone, Deserializable, Eq, Merge, PartialEq))]
#[partial(deserializable(with_validator))]
#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))]
#[partial(serde(deny_unknown_fields))]
#[partial(serde(deny_unknown_fields, rename_all = "camelCase"))]
pub struct VcsConfiguration {
/// Whether we should integrate itself with the VCS client
#[partial(bpaf(long("vcs-enabled"), argument("true|false")))]
Expand All @@ -28,7 +28,7 @@ pub struct VcsConfiguration {
pub use_ignore_file: bool,

/// The folder where we should check for VCS files. By default, we will use the same
/// folder where `pglt.toml` was found.
/// folder where `pglt.jsonc` was found.
///
/// If we can't find the configuration, it will attempt to use the current working directory.
/// If no current working directory can't be found, we won't use the VCS integration, and a diagnostic
Expand Down Expand Up @@ -91,7 +91,7 @@ impl DeserializableValidator for PartialVcsConfiguration {
Clone, Copy, Debug, Default, Deserialize, Deserializable, Eq, Merge, PartialEq, Serialize,
)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
#[serde(rename_all = "camelCase")]
pub enum VcsClientKind {
#[default]
/// Integration with the git client as VCS
Expand Down
Loading