Skip to content

Commit

Permalink
🎉 Hello, Joe!
Browse files Browse the repository at this point in the history
  • Loading branch information
giacomocavalieri committed Aug 8, 2024
0 parents commit 2ecc0b7
Show file tree
Hide file tree
Showing 15 changed files with 3,756 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: test

on:
push:
branches:
- master
- main
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "26.0.2"
gleam-version: "1.4.0-rc1"
rebar3-version: "3"
# elixir-version: "1.15.4"
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.beam
*.ez
/build
erl_crash.dump
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# squirrel

[![Package Version](https://img.shields.io/hexpm/v/squirrel)](https://hex.pm/packages/squirrel)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/squirrel/)

```gleam
import squirrel
pub fn main() {
// TODO: An example of the project in use
}
```

Further documentation can be found at <https://hexdocs.pm/squirrel>.

## Development

```sh
gleam run # Run the project
gleam test # Run the tests
```
23 changes: 23 additions & 0 deletions gleam.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name = "squirrel"
version = "0.1.0"
description = "🐿️ Type safe SQL in Gleam"
licences = ["Apache-2.0"]
repository = { type = "github", user = "giacomocavalieri", repo = "squirrel" }

[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
simplifile = ">= 2.0.1 and < 3.0.0"
eval = ">= 1.0.0 and < 2.0.0"
gleam_json = ">= 1.0.0 and < 2.0.0"
mug = ">= 1.1.0 and < 2.0.0"
glam = ">= 2.0.1 and < 3.0.0"
justin = ">= 1.0.1 and < 2.0.0"
filepath = ">= 1.0.0 and < 2.0.0"
gleam_community_ansi = ">= 1.4.0 and < 2.0.0"
term_size = ">= 1.0.1 and < 2.0.0"
gleam_community_colour = ">= 1.4.0 and < 2.0.0"
argv = ">= 1.0.2 and < 2.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
birdie = ">= 1.1.8 and < 2.0.0"
41 changes: 41 additions & 0 deletions manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# This file was generated by Gleam
# You typically do not need to edit this file

packages = [
{ name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" },
{ name = "birdie", version = "1.1.8", build_tools = ["gleam"], requirements = ["argv", "filepath", "glance", "gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "justin", "rank", "simplifile", "trie_again"], otp_app = "birdie", source = "hex", outer_checksum = "D225C0A3035FCD73A88402925A903AAD3567A1515C9EAE8364F11C17AD1805BB" },
{ name = "eval", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "eval", source = "hex", outer_checksum = "264DAF4B49DF807F303CA4A4E4EBC012070429E40BE384C58FE094C4958F9BDA" },
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
{ name = "glam", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "66EC3BCD632E51EED029678F8DF419659C1E57B1A93D874C5131FE220DFAD2B2" },
{ name = "glance", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "8F3314D27773B7C3B9FB58D8C02C634290422CE531988C0394FA0DF8676B964D" },
{ name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" },
{ name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" },
{ name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" },
{ name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" },
{ name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" },
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
{ name = "glexer", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "BD477AD657C2B637FEF75F2405FAEFFA533F277A74EF1A5E17B55B1178C228FB" },
{ name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" },
{ name = "mug", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "mug", source = "hex", outer_checksum = "85A61E67A7A8C25F4460D9CBEF1C09C68FC06ABBC6FF893B0A1F42AE01CBB546" },
{ name = "rank", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "rank", source = "hex", outer_checksum = "5660E361F0E49CBB714CC57CC4C89C63415D8986F05B2DA0C719D5642FAD91C9" },
{ name = "simplifile", version = "2.0.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "5FFEBD0CAB39BDD343C3E1CCA6438B2848847DC170BA2386DF9D7064F34DF000" },
{ name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" },
{ name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" },
{ name = "trie_again", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "trie_again", source = "hex", outer_checksum = "5B19176F52B1BD98831B57FDC97BD1F88C8A403D6D8C63471407E78598E27184" },
]

[requirements]
argv = { version = ">= 1.0.2 and < 2.0.0" }
birdie = { version = ">= 1.1.8 and < 2.0.0" }
eval = { version = ">= 1.0.0 and < 2.0.0" }
filepath = { version = ">= 1.0.0 and < 2.0.0" }
glam = { version = ">= 2.0.1 and < 3.0.0" }
gleam_community_ansi = { version = ">= 1.4.0 and < 2.0.0" }
gleam_community_colour = { version = ">= 1.4.0 and < 2.0.0" }
gleam_json = { version = ">= 1.0.0 and < 2.0.0" }
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
justin = { version = ">= 1.0.1 and < 2.0.0" }
mug = { version = ">= 1.1.0 and < 2.0.0" }
simplifile = { version = ">= 2.0.1 and < 3.0.0" }
term_size = { version = ">= 1.0.1 and < 2.0.0" }
36 changes: 36 additions & 0 deletions prova.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import gleam/pgo
import decode

/// A row you get from running the `prova` query
/// defined in `prova.sql`.
///
/// > 🐿️ This type definition was generated automatically using v1.0.0 of the
/// > [squirrel package](https://github.com/giacomocavalieri/squirrel).
///
pub type ProvaRow {
ProvaRow(user_id: String, username: String, password: String)
}

/// Runs the `prova` query
/// defined in `prova.sql`.
///
/// > 🐿️ This function was generated automatically using v1.0.0 of the
/// > [squirrel package](https://github.com/giacomocavalieri/squirrel).
///
pub fn prova(db) {
let decoder =
decode.into({
use user_id <- decode.parameter
use username <- decode.parameter
use password <- decode.parameter
ProvaRow(user_id: user_id, username: username, password: password)
})
|> decode.field(0, decode.string)
|> decode.field(1, decode.string)
|> decode.field(2, decode.string)

"-- name: prova
select * from kira_user;
"
|> pgo.execute(db, [], decode.from(decoder, _))
}
2 changes: 2 additions & 0 deletions prova.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- name: prova
select * from kira_user;
133 changes: 133 additions & 0 deletions src/squirrel.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import filepath
import glam/doc
import gleam/bool
import gleam/dict.{type Dict}
import gleam/int
import gleam/io
import gleam/list
import gleam/result
import gleam/string
import gleam_community/ansi
import simplifile
import squirrel/internal/database/postgres
import squirrel/internal/error.{type Error, CannotWriteToFile}
import squirrel/internal/query.{type TypedQuery, type UntypedQuery}
import term_size

// TODO LIST:
// - [ ] tests
// - [ ] read db/user/pwd/port from env vars
// - [ ] How does one decide where the query goes?
// - [ ] How do I deal with duplicate names then?
// - [ ] Somehow it treats int[][] as int[]

const squirrel_version = "v1.0.0"

pub fn main() {
let width = term_size.columns() |> result.unwrap(80)
let connection =
postgres.ConnectionOptions(
..postgres.default_connection(),
user: "giacomocavalieri",
database: "prova",
)

walk("src")
|> run(connection)
|> pretty_report
|> io.println
}

/// TODO)) Comment
fn walk(from: String) -> Dict(String, List(String)) {
case filepath.base_name(from) {
"sql" -> {
let assert Ok(files) = simplifile.read_directory(from)
let files = {
use file <- list.filter_map(files)
use extension <- result.try(filepath.extension(file))
use <- bool.guard(when: extension != "sql", return: Error(Nil))
let file_name = filepath.join(from, file)
case simplifile.is_file(file_name) {
Ok(True) -> Ok(file_name)
Ok(False) | Error(_) -> Error(Nil)
}
}
dict.from_list([#(from, files)])
}

_ -> {
let assert Ok(files) = simplifile.read_directory(from)
let directories = {
use file <- list.filter_map(files)
let file_name = filepath.join(from, file)
case simplifile.is_directory(file_name) {
Ok(True) -> Ok(file_name)
Ok(False) | Error(_) -> Error(Nil)
}
}

list.map(directories, walk)
|> list.fold(from: dict.new(), with: dict.merge)
}
}
}

fn run(
directories: Dict(String, List(String)),
connection: postgres.ConnectionOptions,
) -> Dict(String, #(Int, List(Error))) {
use directory, files <- dict.map_values(directories)

let #(queries, errors) =
list.map(files, query.from_file)
|> result.partition

let #(queries, errors) = case postgres.main(queries, connection) {
Error(error) -> #([], [error, ..errors])
Ok(#(queries, type_errors)) -> #(queries, list.append(errors, type_errors))
}

let output_file =
filepath.directory_name(directory)
|> filepath.join("sql.gleam")

case write_queries(queries, to: output_file) {
Ok(n) -> #(n, errors)
Error(error) -> #(list.length(queries), [error, ..errors])
}
}

fn write_queries(
queries: List(TypedQuery),
to file: String,
) -> Result(Int, Error) {
let directory = filepath.directory_name(file)
let _ = simplifile.create_directory_all(directory)

let imports = "import gleam/pgo\nimport decode\n"
let #(count, code) = {
use #(count, code), query <- list.fold(queries, #(0, imports))
#(count + 1, code <> "\n" <> query.generate_code(squirrel_version, query))
}

let try_write =
simplifile.write(code, to: file)
|> result.map_error(CannotWriteToFile(file, _))

use _ <- result.try(try_write)
Ok(count)
}

// --- PRETTY REPORT PRINTING --------------------------------------------------

fn pretty_report(directories: Dict(String, #(Int, List(Error)))) -> String {
todo
}

fn pluralise(count: Int, singular: String, plural: String) -> String {
case count {
1 -> singular
_ -> plural
}
}
Loading

0 comments on commit 2ecc0b7

Please sign in to comment.