Skip to content
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

Rules: support custom hooks and further configuration with lua #1

Merged
merged 2 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 0 additions & 38 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ trace = []

[dependencies]
clap = { version = "4.5", features = ["derive"] }
mlua = {version = "0.10.2", features = ["lua54", "vendored", "serialize"]}
mlua = {version = "0.10.2", features = ["lua54", "vendored"]}
serde = { version = "1.0", features = ["derive"] }
toml = "0.8.19"
89 changes: 71 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ dynamic correctness. See below for a list of currently implemented features.
- [ ] diagnostics for full sqleibniz analysis
- [ ] snippets
- [ ] intelligent completions
- [ ] lua scripting
- [x] configure sqleibniz with lua
- [x] scripting to hook into node analysis for custom diagnostics
- [ ] execute hooks when encountering the defined node while analysing

### Supported sql statements

Expand Down Expand Up @@ -162,28 +166,77 @@ accessible to sqleibniz by existing at the path sqleibniz is invoked at.
Consult [src/rules.rs](./src/rules.rs) for configuration documentation and
[leibniz.lua](./leibniz.lua) for said example:

```lua
````lua

-- this is an example configuration, consult: https://www.lua.org/manual/5.4/
-- or https://learnxinyminutes.com/docs/lua/ for syntax help and
-- src/rules.rs::Config for all available options
leibniz.disabled_rules = {
-- by default, sqleibniz specific errors are disabled:
"NoContent", -- source file is empty
"NoStatements", -- source file contains no statements
"Unimplemented", -- construct is not implemented yet
"BadSqleibnizInstruction", -- source file contains a bad sqleibniz instruction

-- ignoring sqlite specific diagnostics:

-- "UnknownKeyword", -- an unknown keyword was encountered
-- "UnterminatedString", -- a not closed string was found
-- "UnknownCharacter", -- an unknown character was found
-- "InvalidNumericLiteral", -- an invalid numeric literal was found
-- "InvalidBlob", -- an invalid blob literal was found (either bad hex data or incorrect syntax)
-- "Syntax", -- a structure with incorrect syntax was found
-- "Semicolon", -- a semicolon is missing
leibniz = {
disabled_rules = {
-- ignore sqleibniz specific diagnostics:
"NoContent", -- source file is empty
"NoStatements", -- source file contains no statements
"Unimplemented", -- construct is not implemented yet
"BadSqleibnizInstruction", -- source file contains a bad sqleibniz instruction

-- ignore sqlite specific diagnostics:

-- "UnknownKeyword", -- an unknown keyword was encountered
-- "UnterminatedString", -- a not closed string was found
-- "UnknownCharacter", -- an unknown character was found
-- "InvalidNumericLiteral", -- an invalid numeric literal was found
-- "InvalidBlob", -- an invalid blob literal was found (either bad hex data or incorrect syntax)
-- "Syntax", -- a structure with incorrect syntax was found
-- "Semicolon", -- a semicolon is missing
},
-- sqleibniz allows for writing custom rules with lua
hooks = {
{
-- summarises the hooks content
name = "idents should be lowercase",
-- instructs sqleibniz which node to execute the `hook` for
node = "literal",
-- sqleibniz calls the hook function once it encounters a node name
-- matching the hook.node content
--
-- The `node` argument holds the following fields:
--
--```
--node: {
-- kind: string,
-- text: string,
-- children: node[],
--}
--```
--
hook = function(node)
if node.kind == "ident" then
if string.match(node.text, "%u") then
-- returing an error passes the diagnostic to sqleibniz,
-- thus a pretty message with the name of the hook, the
-- node it occurs and the message passed to error() is
-- generated
error("All idents should be lowercase")
end
end
end
},
{
name = "idents shouldn't be longer than 12 characters",
node = "literal",
hook = function(node)
local max_size = 12
if node.kind == "ident" then
if string.len(node.text) >= max_size then
error("idents shouldn't be longer than " .. max_size .. " characters")
end
end
end
}
}
}
```

````

### sqleibniz instructions

Expand Down
75 changes: 61 additions & 14 deletions leibniz.lua
Original file line number Diff line number Diff line change
@@ -1,20 +1,67 @@
-- this is an example configuration, consult: https://www.lua.org/manual/5.4/
-- or https://learnxinyminutes.com/docs/lua/ for syntax help and
-- src/rules.rs::Config for all available options
leibniz.disabled_rules = {
-- by default, sqleibniz specific errors are disabled:
"NoContent", -- source file is empty
"NoStatements", -- source file contains no statements
"Unimplemented", -- construct is not implemented yet
"BadSqleibnizInstruction", -- source file contains a bad sqleibniz instruction
leibniz = {
disabled_rules = {
-- ignore sqleibniz specific diagnostics:
"NoContent", -- source file is empty
"NoStatements", -- source file contains no statements
"Unimplemented", -- construct is not implemented yet
"BadSqleibnizInstruction", -- source file contains a bad sqleibniz instruction

-- ignoring sqlite specific diagnostics:
-- ignore sqlite specific diagnostics:

-- "UnknownKeyword", -- an unknown keyword was encountered
-- "UnterminatedString", -- a not closed string was found
-- "UnknownCharacter", -- an unknown character was found
-- "InvalidNumericLiteral", -- an invalid numeric literal was found
-- "InvalidBlob", -- an invalid blob literal was found (either bad hex data or incorrect syntax)
-- "Syntax", -- a structure with incorrect syntax was found
-- "Semicolon", -- a semicolon is missing
-- "UnknownKeyword", -- an unknown keyword was encountered
-- "UnterminatedString", -- a not closed string was found
-- "UnknownCharacter", -- an unknown character was found
-- "InvalidNumericLiteral", -- an invalid numeric literal was found
-- "InvalidBlob", -- an invalid blob literal was found (either bad hex data or incorrect syntax)
-- "Syntax", -- a structure with incorrect syntax was found
-- "Semicolon", -- a semicolon is missing
},
-- sqleibniz allows for writing custom rules with lua
hooks = {
{
-- summarises the hooks content
name = "idents should be lowercase",
-- instructs sqleibniz which node to execute the `hook` for
node = "literal",
-- sqleibniz calls the hook function once it encounters a node name
-- matching the hook.node content
--
-- The `node` argument holds the following fields:
--
--```
--node: {
-- kind: string,
-- text: string,
-- children: node[],
--}
--```
--
hook = function(node)
if node.kind == "ident" then
if string.match(node.text, "%u") then
-- returing an error passes the diagnostic to sqleibniz,
-- thus a pretty message with the name of the hook, the
-- node it occurs and the message passed to error() is
-- generated
error("All idents should be lowercase")
end
end
end
},
{
name = "idents shouldn't be longer than 12 characters",
node = "literal",
hook = function(node)
local max_size = 12
if node.kind == "ident" then
if string.len(node.text) >= max_size then
error("idents shouldn't be longer than " .. max_size .. " characters")
end
end
end
}
}
}
23 changes: 13 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{fs, process::exit, vec};
use clap::Parser;
use error::{print_str_colored, warn};
use lexer::Lexer;
use mlua::{Lua, LuaSerdeExt};
use mlua::Lua;
use rules::{Config, Rule};

/// error does formatting and highlighting for errors
Expand Down Expand Up @@ -66,27 +66,30 @@ fn main() {

let mut config = Config {
disabled_rules: vec![],
hooks: None,
};

if !args.ignore_config {
match fs::read_to_string(&args.config) {
Ok(config_str) => {
let lua = Lua::new();
let globals = lua.globals();
let leibniz = lua
.to_value(&Config {
disabled_rules: vec![],
})
.expect("failed to serialize default configuration");
globals
.set("leibniz", leibniz)
.set(
"leibniz",
Config {
disabled_rules: vec![],
hooks: None,
},
)
.expect("failed to serialize default configuration");
match lua.load(config_str).set_name(&args.config).exec() {
Ok(()) => {
if let Ok(raw_conf) = globals.get::<mlua::Value>("leibniz") {
match lua.from_value::<Config>(raw_conf) {
Ok(conf) => config.disabled_rules = conf.disabled_rules,
Err(err) => println!("{err}"),
let c: mlua::Result<Config> = lua.unpack(raw_conf);
match c {
Ok(conf) => config = conf,
Err(err) => println!("{}: {}", &args.config, err),
}
}
}
Expand Down
Loading
Loading