Add 'development/libs/barrel/' from commit 'f3b8ab47d3a3ad8d43dc2b89a5eec1c4e87b033d'

git-subtree-dir: development/libs/barrel
git-subtree-mainline: 518551bfa6ef7d6508b425afa4bfb3ddbd418141
git-subtree-split: f3b8ab47d3a3ad8d43dc2b89a5eec1c4e87b033d
wip/yesman
Katharina Fey 4 years ago
parent 534807c7a3
commit a9f4dd41c1
  1. 1
      development/libs/barrel/.envrc
  2. 4
      development/libs/barrel/.gitignore
  3. 48
      development/libs/barrel/.travis.yml
  4. 46
      development/libs/barrel/Cargo.toml
  5. 21
      development/libs/barrel/LICENSE
  6. 78
      development/libs/barrel/README.md
  7. 14
      development/libs/barrel/appveyor.yml
  8. 173
      development/libs/barrel/assets/logo.svg
  9. 4
      development/libs/barrel/barrel_derives/.gitignore
  10. 11
      development/libs/barrel/barrel_derives/Cargo.toml
  11. 81
      development/libs/barrel/barrel_derives/src/lib.rs
  12. 76
      development/libs/barrel/code_of_conduct.md
  13. 4
      development/libs/barrel/docs/.gitignore
  14. 22
      development/libs/barrel/docs/LICENSE
  15. 124
      development/libs/barrel/docs/Makefile
  16. 3
      development/libs/barrel/docs/README.md
  17. 16
      development/libs/barrel/docs/blue-penguin/CONTRIBUTORS.md
  18. 53
      development/libs/barrel/docs/blue-penguin/README.md
  19. BIN
      development/libs/barrel/docs/blue-penguin/screenshot.png
  20. 4
      development/libs/barrel/docs/blue-penguin/static/css/print.css
  21. 87
      development/libs/barrel/docs/blue-penguin/static/css/pygments.css
  22. 342
      development/libs/barrel/docs/blue-penguin/static/css/screen.css
  23. 11
      development/libs/barrel/docs/blue-penguin/templates/analytics.html
  24. 22
      development/libs/barrel/docs/blue-penguin/templates/archives.html
  25. 17
      development/libs/barrel/docs/blue-penguin/templates/article.html
  26. 37
      development/libs/barrel/docs/blue-penguin/templates/article_stub.html
  27. 7
      development/libs/barrel/docs/blue-penguin/templates/author.html
  28. 105
      development/libs/barrel/docs/blue-penguin/templates/base.html
  29. 6
      development/libs/barrel/docs/blue-penguin/templates/category.html
  30. 12
      development/libs/barrel/docs/blue-penguin/templates/disqus.html
  31. 17
      development/libs/barrel/docs/blue-penguin/templates/index.html
  32. 8
      development/libs/barrel/docs/blue-penguin/templates/page.html
  33. 38
      development/libs/barrel/docs/blue-penguin/templates/pagination.html
  34. 5
      development/libs/barrel/docs/blue-penguin/templates/tag.html
  35. 8
      development/libs/barrel/docs/blue-penguin/templates/tags.html
  36. 6
      development/libs/barrel/docs/blue-penguin/templates/translations.html
  37. 59
      development/libs/barrel/docs/content/blog/001_hello_barrel.md
  38. 30
      development/libs/barrel/docs/content/images/logo.svg
  39. 74
      development/libs/barrel/docs/content/pages/home.md
  40. 103
      development/libs/barrel/docs/develop_server.sh
  41. 94
      development/libs/barrel/docs/fabfile.py
  42. 77
      development/libs/barrel/docs/pelicanconf.py
  43. 21
      development/libs/barrel/examples/pg_strings.rs
  44. 17
      development/libs/barrel/examples/sqlite_strings.rs
  45. 17
      development/libs/barrel/examples/user_index.rs
  46. 48
      development/libs/barrel/guides/diesel-setup.md
  47. 8
      development/libs/barrel/shell.nix
  48. 42
      development/libs/barrel/src/TODO
  49. 89
      development/libs/barrel/src/backend/mod.rs
  50. 162
      development/libs/barrel/src/backend/mysql.rs
  51. 164
      development/libs/barrel/src/backend/pg.rs
  52. 155
      development/libs/barrel/src/backend/sqlite3.rs
  53. 19
      development/libs/barrel/src/connectors.rs
  54. 197
      development/libs/barrel/src/integrations/diesel.rs
  55. 6
      development/libs/barrel/src/integrations/mod.rs
  56. 176
      development/libs/barrel/src/lib.rs
  57. 221
      development/libs/barrel/src/migration.rs
  58. 29
      development/libs/barrel/src/schema.rs
  59. 177
      development/libs/barrel/src/table.rs
  60. 91
      development/libs/barrel/src/tests/common/cloning.rs
  61. 129
      development/libs/barrel/src/tests/common/mod.rs
  62. 19
      development/libs/barrel/src/tests/common/runtime.rs
  63. 16
      development/libs/barrel/src/tests/common/utils.rs
  64. 13
      development/libs/barrel/src/tests/mod.rs
  65. 65
      development/libs/barrel/src/tests/mysql/add_column.rs
  66. 37
      development/libs/barrel/src/tests/mysql/create_table.rs
  67. 5
      development/libs/barrel/src/tests/mysql/mod.rs
  68. 50
      development/libs/barrel/src/tests/mysql/simple.rs
  69. 165
      development/libs/barrel/src/tests/pg/add_column.rs
  70. 134
      development/libs/barrel/src/tests/pg/create_table.rs
  71. 6
      development/libs/barrel/src/tests/pg/mod.rs
  72. 19
      development/libs/barrel/src/tests/pg/reference.rs
  73. 70
      development/libs/barrel/src/tests/pg/simple.rs
  74. 37
      development/libs/barrel/src/tests/sqlite3/create_table.rs
  75. 4
      development/libs/barrel/src/tests/sqlite3/mod.rs
  76. 56
      development/libs/barrel/src/tests/sqlite3/simple.rs
  77. 116
      development/libs/barrel/src/types/builders.rs
  78. 89
      development/libs/barrel/src/types/defaults.rs
  79. 165
      development/libs/barrel/src/types/impls.rs
  80. 9
      development/libs/barrel/src/types/mod.rs

@ -0,0 +1 @@
eval "$(lorri direnv)"

@ -0,0 +1,4 @@
/target/
**/*.rs.bk
Cargo.lock

@ -0,0 +1,48 @@
# Some basic stuff about what we're doing here
dist: trusty
language: rust
services: docker
sudo: required
cache: cargo
rust:
# - stable # Only build on nightly rust for now
- nightly
# This is required for coveralls
addons:
apt:
packages:
- libcurl4-openssl-dev
- libelf-dev
- libdw-dev
- binutils-dev
- cmake
sources:
- kalakris-cmake
# If nightly explodes we don't care aaas much
matrix:
allow_failures:
# fail on nightly while we don't have a stable build
# - rust: nightly
# This is a pretty big hack and only really needed on the first of a build chain
before_script:
- cargo install cargo-travis -f && export PATH=$HOME/.cargo/bin:$PATH
# Build, test, benchmark, document. Gogogogo!
script:
- cargo build --verbose --all --features "pg mysql sqlite3 unstable"
- cargo test --verbose --all --features "pg mysql sqlite3 unstable"
- cargo test --verbose --all --features "diesel pg unstable" # Diesel module _demands_ only one backend
- cargo doc --features "pg mysql sqlite3 unstable"
# Upload the whole mess
after_success:
- cargo coveralls --verbose --features "pg mysql sqlite3 unstable"
# AND GOD DAMN IT LET ME SLEEP!
notifications:
email:
on_success: never
on_failure: never

@ -0,0 +1,46 @@
[package]
name = "barrel"
version = "0.6.6-alpha.0"
description = "A powerful schema migration building API for Rust"
authors = ["Katharina Fey <kookie@spacekookie.de>", "Rob Rowe <rippinrobr@gmail.com>"]
license = "MIT/X11 OR Apache-2.0"
edition = "2018"
readme = "README.md"
repository = "https://github.com/rust-db/barrel"
homepage = "https://rust-db.github.io/barrel"
documentation = "https://docs.rs/barrel"
categories = [ "database", "development-tools"]
keywords = ["sql", "database", "schema", "migration"]
[package.metadata.docs.rs]
# We can't build documentation with the `diesel` flag enabled
# because then the project no longer builds.
features = ["mysql", "sqlite3", "pg", "unstable"]
[[example]]
name = "pg_strings"
required-features = ["pg"]
[[example]]
name = "sqlite_strings"
required-features = ["sqlite3"]
[features]
default = []
diesel = ["tempfile", "diesel_rs"]
sqlite3 = []
mysql = []
pg = []
# Enables unstable (in-development) features,
# even for stable version upgrades
unstable = []
[dependencies]
tempfile = { version = "3", optional = true }
diesel_rs = { version = ">= 1.2, < 2.0", package = "diesel", default_features = false, optional = true }

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Katharina Fey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,78 @@
![](assets/logo.svg)
[![](https://travis-ci.org/rust-db/barrel.svg?branch=master)](https://travis-ci.org/rust-db/barrel)
[![](https://ci.appveyor.com/api/projects/status/7e00r2e1xatxk3bj?svg=true)](https://ci.appveyor.com/project/spacekookie/barrel)
[![](https://coveralls.io/repos/github/rust-db/barrel/badge.svg?branch=master&service=github)](https://coveralls.io/github/rust-db/barrel?branch=master)
[![](https://docs.rs/barrel/badge.svg)](https://docs.rs/barrel/)
[![](https://img.shields.io/crates/v/barrel.svg)](https://crates.io/crates/barrel)
[![](https://img.shields.io/crates/d/barrel.svg)](https://crates.io/crates/barrel)
A powerful database schema builder, that lets you write your SQL
migrations in Rust!
`barrel` offers callback-style builder functions for SQL migrations
and is designed to be flexible, portable and fun to use. It provides
you with a common interface over SQL, with additional
database-specific builders.
This way you can focus on your Rust code, without having to worry
about SQL.
## Example
The following example will help you get started
```rust
use barrel::{types, Migration};
use barrel::backend::Pg;
fn main() {
let mut m = Migration::new();
m.create_table("users", |t| {
t.add_column("name", types::varchar(255));
t.add_column("age", types::integer());
t.add_column("owns_plushy_sharks", types::boolean());
});
println!("{}", m.make::<Pg>());
}
```
## Using Diesel
Since `diesel 1.2.0` it's possible to now use `barrel` for migrations
with `diesel`. A guide with some more information on how to get
started can be found
[here](https://github.com/spacekookie/barrel/blob/master/guides/diesel-setup.md)
### Migration guide
If you've been using `barrel` to write migrations for `diesel` before
the `0.5.0` release, some migration of your migrations will be
required. Since `0.5.0` the way types are constructed changed.
Instead of constructing a type with `Types::VarChar(255)` (an enum
variant), the types are now provided by a module called `types` and
builder functions. The same type would now be `types::varchar(255)`
(a function call), which then returns a `Type` enum.
You can also directly created your own `Type` builders this way.
Check the docs for details!
## License
`barrel` is free software: you can redistribute it and/or modify it
under the terms of the MIT Public License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MIT
Public License for more details.
## Conduct
In the interest of fostering an open and welcoming environment, the
`barrel` project pledges to making participation a harassment-free
experience for everyone. See [Code of Conduct](code_of_conduct.md)
for details. In case of violations, e-mail
[kookie@spacekookie.de](mailto:kookie@spacekookie.de).

@ -0,0 +1,14 @@
build: false
build_script:
- cargo test --verbose %cargoflags% --all-features
environment:
matrix:
- channel: stable
target: x86_64-pc-windows-gnu
install:
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init -yv --default-toolchain %channel% --default-host %target%
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
- rustc -vV
- cargo -vV

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="10cm"
height="4.5cm"
viewBox="0 0 100 44.999997"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="logo.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.6650702"
inkscape:cx="161.02141"
inkscape:cy="72.370108"
inkscape:document-units="mm"
inkscape:current-layer="g839"
showgrid="false"
inkscape:window-width="1914"
inkscape:window-height="1032"
inkscape:window-x="0"
inkscape:window-y="22"
inkscape:window-maximized="0"
showguides="false"
units="cm" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-252.00026)">
<g
aria-label="barrel"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20.37609673px;line-height:1.25;font-family:Quicksand;-inkscape-font-specification:'Quicksand Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.50940239"
id="text968">
<path
d="m 41.584764,271.53284 q 1.445345,0 2.605424,0.70366 1.179097,0.68464 1.844716,1.90177 0.684637,1.19811 0.684637,2.68149 0,1.48338 -0.703655,2.70051 -0.684637,1.21714 -1.901769,1.92079 -1.198114,0.68464 -2.681494,0.68464 -1.48338,0 -2.700512,-0.68464 -1.198115,-0.70365 -1.901769,-1.92079 -0.684637,-1.21713 -0.684637,-2.70051 v -4.46916 q 0,-0.1141 0.01902,-0.17115 v -4.25997 q 0,-0.3233 0.190177,-0.53249 0.209195,-0.2092 0.532496,-0.2092 0.3233,0 0.532495,0.2092 0.209195,0.20919 0.209195,0.53249 v 5.57219 q 0.665619,-0.91285 1.692574,-1.42633 1.026955,-0.5325 2.263105,-0.5325 z m -0.152141,9.26162 q 1.084008,0 1.958822,-0.51348 0.893831,-0.53249 1.388291,-1.42633 0.513478,-0.91284 0.513478,-2.03489 0,-1.12204 -0.513478,-2.01587 -0.49446,-0.91285 -1.388291,-1.42633 -0.874814,-0.51348 -1.958822,-0.51348 -1.502398,0 -2.567389,0.95089 -1.045973,0.95088 -1.236149,2.43426 0,0.0571 -0.01902,0.0951 -0.01902,0.15214 -0.01902,0.47544 0,1.12205 0.49446,2.03489 0.513478,0.89384 1.388292,1.42633 0.874813,0.51348 1.958822,0.51348 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;stroke-width:0.50940239"
id="path841"
inkscape:connector-curvature="0" />
<path
d="m 54.327211,271.53284 q 1.48338,0 2.681494,0.70366 1.217132,0.68464 1.901769,1.90177 0.703655,1.21713 0.703655,2.70051 v 4.46916 q 0,0.30428 -0.209195,0.51347 -0.209194,0.2092 -0.532495,0.2092 -0.323301,0 -0.532495,-0.19018 -0.209195,-0.20919 -0.209195,-0.53249 v -1.14106 q -0.665619,0.91284 -1.692574,1.44534 -1.026956,0.51348 -2.263106,0.51348 -1.445344,0 -2.624441,-0.68464 -1.160079,-0.70365 -1.844716,-1.90177 -0.665619,-1.21713 -0.665619,-2.70051 0,-1.48338 0.684637,-2.70051 0.703654,-1.21713 1.901769,-1.90177 1.217132,-0.70366 2.700512,-0.70366 z m 0,9.26162 q 1.084008,0 1.958822,-0.51348 0.874814,-0.51348 1.369274,-1.40731 0.513477,-0.91285 0.513477,-2.03489 0,-1.12204 -0.513477,-2.01588 -0.49446,-0.91284 -1.369274,-1.42632 -0.874814,-0.5325 -1.958822,-0.5325 -1.084008,0 -1.97784,0.5325 -0.874814,0.51348 -1.388291,1.42632 -0.49446,0.89384 -0.49446,2.01588 0,1.12204 0.49446,2.03489 0.513477,0.89383 1.388291,1.40731 0.893832,0.51348 1.97784,0.51348 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;stroke-width:0.50940239"
id="path843"
inkscape:connector-curvature="0" />
<path
d="m 67.178119,271.51383 q 0.798743,0 1.255168,0.20919 0.475442,0.2092 0.475442,0.58955 0,0.11411 -0.01902,0.17116 -0.07607,0.26625 -0.24723,0.36133 -0.152141,0.0951 -0.437407,0.0951 -0.171159,0 -0.589548,-0.038 -0.152141,-0.019 -0.456425,-0.019 -1.426326,0 -2.339175,0.76071 -0.893832,0.76071 -0.893832,1.97784 v 5.68629 q 0,0.34232 -0.190177,0.53249 -0.190177,0.19018 -0.532495,0.19018 -0.342319,0 -0.532496,-0.19018 -0.190176,-0.19017 -0.190176,-0.53249 v -8.95734 q 0,-0.34231 0.190176,-0.53249 0.190177,-0.19018 0.532496,-0.19018 0.342318,0 0.532495,0.19018 0.190177,0.19018 0.190177,0.53249 v 0.83678 q 0.551513,-0.79874 1.407309,-1.23615 0.855796,-0.4374 1.844716,-0.4374 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;stroke-width:0.50940239"
id="path845"
inkscape:connector-curvature="0" />
<path
d="m 75.90694,271.51383 q 0.798743,0 1.255168,0.20919 0.475442,0.2092 0.475442,0.58955 0,0.11411 -0.01902,0.17116 -0.07607,0.26625 -0.24723,0.36133 -0.152141,0.0951 -0.437407,0.0951 -0.171159,0 -0.589548,-0.038 -0.152142,-0.019 -0.456425,-0.019 -1.426327,0 -2.339176,0.76071 -0.893831,0.76071 -0.893831,1.97784 v 5.68629 q 0,0.34232 -0.190177,0.53249 -0.190177,0.19018 -0.532495,0.19018 -0.342319,0 -0.532496,-0.19018 -0.190177,-0.19017 -0.190177,-0.53249 v -8.95734 q 0,-0.34231 0.190177,-0.53249 0.190177,-0.19018 0.532496,-0.19018 0.342318,0 0.532495,0.19018 0.190177,0.19018 0.190177,0.53249 v 0.83678 q 0.551513,-0.79874 1.407309,-1.23615 0.855796,-0.4374 1.844716,-0.4374 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;stroke-width:0.50940239"
id="path847"
inkscape:connector-curvature="0" />
<path
d="m 83.366629,271.53284 q 1.407309,0 2.491318,0.66562 1.084008,0.6466 1.673556,1.80668 0.608567,1.16008 0.608567,2.64346 0,0.30429 -0.190177,0.49446 -0.190177,0.17116 -0.49446,0.17116 h -7.683147 q 0.152141,1.55945 1.217132,2.52935 1.064991,0.96991 2.662477,0.96991 0.684636,0 1.369273,-0.22822 0.703655,-0.24723 1.122044,-0.60856 0.24723,-0.19018 0.513478,-0.19018 0.209194,0 0.418389,0.15214 0.24723,0.22822 0.24723,0.51348 0,0.24723 -0.209195,0.43741 -0.627584,0.53249 -1.597486,0.89383 -0.969902,0.34232 -1.863733,0.34232 -1.521416,0 -2.71953,-0.66562 -1.198115,-0.68464 -1.882752,-1.88275 -0.665619,-1.21714 -0.665619,-2.73855 0,-1.54043 0.627584,-2.73855 0.646602,-1.21713 1.768645,-1.88275 1.141062,-0.68464 2.586406,-0.68464 z m 0,1.31222 q -1.445344,0 -2.396229,0.89384 -0.950884,0.89383 -1.160079,2.35819 h 6.960475 q -0.152142,-1.46436 -1.064991,-2.35819 -0.912849,-0.89384 -2.339176,-0.89384 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;stroke-width:0.50940239"
id="path849"
inkscape:connector-curvature="0" />
<path
d="m 92.842788,282.03061 q -1.16008,0 -1.90177,-0.91285 -0.722672,-0.93187 -0.722672,-2.37721 v -10.84009 q 0,-0.3233 0.209195,-0.51347 0.209194,-0.2092 0.513477,-0.2092 0.323301,0 0.513478,0.2092 0.209195,0.19017 0.209195,0.51347 v 10.84009 q 0,0.81776 0.3233,1.33124 0.342319,0.51347 0.855797,0.51347 h 0.475442 q 0.285265,0 0.456424,0.2092 0.17116,0.19018 0.17116,0.51348 0,0.3233 -0.24723,0.53249 -0.228213,0.19018 -0.608567,0.19018 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;stroke-width:0.50940239"
id="path851"
inkscape:connector-curvature="0" />
</g>
<g
id="g839"
transform="translate(0,0.0917458)">
<rect
y="256.67191"
x="3.3755143"
height="36.679298"
width="24.888735"
id="rect815-6-7"
style="opacity:1;fill:#666666;fill-opacity:1;stroke:none;stroke-width:2.76557231;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;paint-order:fill markers stroke" />
<rect
y="255.34891"
x="2.7532961"
height="1.5555459"
width="26.133173"
id="rect815"
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.7789228;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;paint-order:fill markers stroke" />
<rect
y="292.68201"
x="2.7532961"
height="1.5555459"
width="26.133173"
id="rect815-3"
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.7789228;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;paint-order:fill markers stroke" />
<rect
y="268.10437"
x="3.3755143"
height="0.62221843"
width="24.888735"
id="rect815-5"
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.74082875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;paint-order:fill markers stroke" />
<rect
y="281.17096"
x="3.3755143"
height="0.62221843"
width="24.888735"
id="rect815-5-5"
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.74082875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;paint-order:fill markers stroke" />
<rect
y="255.37721"
x="16.446571"
height="38.852245"
width="11.822151"
id="rect815-6-8"
style="opacity:0.55;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:2.68609262;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;paint-order:fill markers stroke" />
<rect
transform="rotate(-45)"
y="200.44443"
x="-188.45515"
height="11.199931"
width="11.199931"
id="rect909"
style="opacity:1;fill:#e6e6e6;fill-opacity:1;stroke:#868686;stroke-width:0.62221843;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
<text
id="text913"
y="276.47345"
x="10.648504"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.76389503px;line-height:1.25;font-family:Quicksand;-inkscape-font-specification:'Quicksand Bold';letter-spacing:0px;word-spacing:0px;fill:#202020;fill-opacity:1;stroke:none;stroke-width:0.3190974"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.1055584px;font-family:Quicksand;-inkscape-font-specification:'Quicksand Bold';fill:#202020;fill-opacity:1;stroke-width:0.3190974"
y="276.47345"
x="10.648504"
id="tspan911"
sodipodi:role="line">SQL</tspan></text>
<rect
y="254.88242"
x="21.650042"
height="1.5555459"
width="2.9549315"
id="rect815-4"
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.26192206;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;paint-order:fill markers stroke" />
<rect
y="255.37721"
x="16.446571"
height="38.852242"
width="11.822153"
id="rect815-6-8-0"
style="opacity:0.1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:2.68609262;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;paint-order:fill markers stroke" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

@ -0,0 +1,4 @@
/target/
**/*.rs.bk
Cargo.lock

@ -0,0 +1,11 @@
[package]
name = "barrel_derives"
version = "0.0.0"
authors = ["Katharina Fey <kookie@spacekookie.de>"]
[lib]
proc-macro = true
[dependencies]
syn = { version = "^0.12", features = ["extra-traits"] }
quote = "^0.4"

@ -0,0 +1,81 @@
//! A crate that handles some of the finer magical points of barrel error handling
//!
//! The idea behind this is that most programmer errors will be caught when
//! compiling the migrations, instead of during the runtime of a migration.
//!
//! This means less hassle on your production system and better testability 🎉
//!
//! More docs will follow here as the module matures
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
use syn::DeriveInput;
#[proc_macro_derive(TextType)]
pub fn typed(input: TokenStream) -> TokenStream {
// Parse the input tokens into a syntax tree
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
panic!("name: {}", name);
// Build the output, possibly using quasi-quotation
let expanded = quote! {
};
// Hand the output tokens back to the compiler
expanded.into()
}
// extern crate proc_macro;
// extern crate syn;
// #[macro_use]
// extern crate quote;
// use proc_macro::TokenStream;
// #[proc_macro_derive(Typed)]
// pub fn column_type_check(input: TokenStream) -> TokenStream {
// let s = input.to_string();
// let ast = syn::parse(input).unwrap();
// panic!(s);
// // unimplemented!();
// }
// #[proc_macro_derive(HelloWorld)]
// pub fn hello_world(input: TokenStream) -> TokenStream {
// // Construct a string representation of the type definition
// let s = input.to_string();
// // Parse the string representation
// let ast = syn::parse_derive_input(&s).unwrap();
// // Build the impl
// let gen = impl_hello_world(&ast);
// // Return the generated impl
// gen.parse().unwrap()
// }
// fn impl_hello_world(ast: &syn::DeriveInput) -> quote::Tokens {
// let name = &ast.ident;
// quote! {
// impl HelloWorld for #name {
// fn hello_world() {
// println!("Hello, World! My name is {}", stringify!(#name));
// }
// }
// }
// }

@ -0,0 +1,76 @@
# Contributor Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer at kookie@spacekookie.de
All complaints will be reviewed and investigated and will result in a response
that is deemed necessary and appropriate to the circumstances. The project team
is obligated to maintain confidentiality with regard to the reporter of an
incident. Further details of specific enforcement policies may be posted
separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [https://contributor-covenant.org/version/1/4][version]
[homepage]: https://contributor-covenant.org
[version]: https://contributor-covenant.org/version/1/4/

@ -0,0 +1,4 @@
output
**/*.pyc
env
*.pid

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2018 Katharina
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the Software), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,124 @@
PY?=python3
PELICAN?=pelican
PELICANOPTS=
BASEDIR=$(CURDIR)
INPUTDIR=$(BASEDIR)/content
OUTPUTDIR=$(BASEDIR)/output
CONFFILE=$(BASEDIR)/pelicanconf.py
PUBLISHCONF=$(BASEDIR)/publishconf.py
FTP_HOST=localhost
FTP_USER=anonymous
FTP_TARGET_DIR=/
SSH_HOST=localhost
SSH_PORT=22
SSH_USER=root
SSH_TARGET_DIR=/var/www
S3_BUCKET=my_s3_bucket
CLOUDFILES_USERNAME=my_rackspace_username
CLOUDFILES_API_KEY=my_rackspace_api_key
CLOUDFILES_CONTAINER=my_cloudfiles_container
DROPBOX_DIR=~/Dropbox/Public/
GITHUB_PAGES_BRANCH=gh-pages
DEBUG ?= 0
ifeq ($(DEBUG), 1)
PELICANOPTS += -D
endif
RELATIVE ?= 0
ifeq ($(RELATIVE), 1)
PELICANOPTS += --relative-urls
endif
help:
@echo 'Makefile for a pelican Web site '
@echo ' '
@echo 'Usage: '
@echo ' make html (re)generate the web site '
@echo ' make clean remove the generated files '
@echo ' make regenerate regenerate files upon modification '
@echo ' make publish generate using production settings '
@echo ' make serve [PORT=8000] serve site at http://localhost:8000'
@echo ' make serve-global [SERVER=0.0.0.0] serve (as root) to $(SERVER):80 '
@echo ' make devserver [PORT=8000] start/restart develop_server.sh '
@echo ' make stopserver stop local server '
@echo ' make ssh_upload upload the web site via SSH '
@echo ' make rsync_upload upload the web site via rsync+ssh '
@echo ' make dropbox_upload upload the web site via Dropbox '
@echo ' make ftp_upload upload the web site via FTP '
@echo ' make s3_upload upload the web site via S3 '
@echo ' make cf_upload upload the web site via Cloud Files'
@echo ' make github upload the web site via gh-pages '
@echo ' '
@echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html '
@echo 'Set the RELATIVE variable to 1 to enable relative urls '
@echo ' '
html:
$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
clean:
[ ! -d $(OUTPUTDIR) ] || rm -rf $(OUTPUTDIR)
regenerate:
$(PELICAN) -r $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
serve:
ifdef PORT
cd $(OUTPUTDIR) && $(PY) -m pelican.server $(PORT)
else
cd $(OUTPUTDIR) && $(PY) -m pelican.server
endif
serve-global:
ifdef SERVER
cd $(OUTPUTDIR) && $(PY) -m pelican.server 80 $(SERVER)
else
cd $(OUTPUTDIR) && $(PY) -m pelican.server 80 0.0.0.0
endif
devserver:
ifdef PORT
$(BASEDIR)/develop_server.sh restart $(PORT) > /dev/null
else
$(BASEDIR)/develop_server.sh restart > /dev/null
endif
stopserver:
$(BASEDIR)/develop_server.sh stop
@echo 'Stopped Pelican and SimpleHTTPServer processes running in background.'
publish:
$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS)
ssh_upload: publish
scp -P $(SSH_PORT) -r $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
rsync_upload: publish
rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) --cvs-exclude
dropbox_upload: publish
cp -r $(OUTPUTDIR)/* $(DROPBOX_DIR)
ftp_upload: publish
lftp ftp://$(FTP_USER)@$(FTP_HOST) -e "mirror -R $(OUTPUTDIR) $(FTP_TARGET_DIR) ; quit"
s3_upload: publish
s3cmd sync $(OUTPUTDIR)/ s3://$(S3_BUCKET) --acl-public --delete-removed --guess-mime-type
cf_upload: publish
cd $(OUTPUTDIR) && swift -v -A https://auth.api.rackspacecloud.com/v1.0 -U $(CLOUDFILES_USERNAME) -K $(CLOUDFILES_API_KEY) upload -c $(CLOUDFILES_CONTAINER) .
github: publish
ghp-import -m "Generate Pelican site" -b $(GITHUB_PAGES_BRANCH) $(OUTPUTDIR)
git push origin $(GITHUB_PAGES_BRANCH)
.PHONY: html help clean regenerate serve serve-global devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github

@ -0,0 +1,16 @@
# Contributors
* [Nevan Scott](https://github.com/nevanscott/Mockingbird) (original author)
* [wrl](http://ghttps://github.com/guikcdithub.com/wrl) (port to pelican, pelican-mockingbird)
* [Jody Frankowski](http://github.com/jody-frankowski) (Blue Penguin)
* [Grimbox](https://github.com/Grimbox)
* [ix5](https://github.com/ix5)
* [dn0](https://github.com/dn0)
* [anhtuann](https://github.com/anhtuann)
* [aperep](https://github.com/aperep)
* [iranzo](https://github.com/iranzo)
* [thetlk](https://github.com/thetlk)
* [SnorlaxYum](https://github.com/SnorlaxYum)
* [guikcd](https://github.com/guikcd)
* [jorgesumle](https://github.com/jorgesumle)
* [crxxn](https://github.com/crxxn)

@ -0,0 +1,53 @@
![screenshot](screenshot.png)
# Blue Penguin for pelican
A simple theme for pelican. Solarized pygments. Feeds support.
## Settings
```python
# all the following settings are *optional*
# HTML metadata
SITEDESCRIPTION = ''
# all defaults to True.
DISPLAY_HEADER = True
DISPLAY_FOOTER = True
DISPLAY_HOME = True
DISPLAY_MENU = True
# provided as examples, they make ‘clean’ urls. used by MENU_INTERNAL_PAGES.
TAGS_URL = 'tags'
TAGS_SAVE_AS = 'tags/index.html'
AUTHORS_URL = 'authors'
AUTHORS_SAVE_AS = 'authors/index.html'
CATEGORIES_URL = 'categories'
CATEGORIES_SAVE_AS = 'categories/index.html'
ARCHIVES_URL = 'archives'
ARCHIVES_SAVE_AS = 'archives/index.html'
# use those if you want pelican standard pages to appear in your menu
MENU_INTERNAL_PAGES = (
('Tags', TAGS_URL, TAGS_SAVE_AS),
('Authors', AUTHORS_URL, AUTHORS_SAVE_AS),
('Categories', CATEGORIES_URL, CATEGORIES_SAVE_AS),
('Archives', ARCHIVES_URL, ARCHIVES_SAVE_AS),
)
# additional menu items
MENUITEMS = (
('GitHub', 'https://github.com/'),
('Linux Kernel', 'https://www.kernel.org/'),
)
```
## How to contribute
Contributions are very welcome. Keep in mind that this theme goal is to be
minimalistic/simple. Contributions will be accepted through Github Pull
Requests. If you don’t have a Github account you can suggest me your
changes by email (which you can find on my github profile).
## Contributors
See [CONTRIBUTORS.md](CONTRIBUTORS.md).
## License
Public domain.

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 KiB

@ -0,0 +1,4 @@
* { background: #fff; }
body { font-family: georgia, times, serif; color: black; }
blockquote { font-style: italic; color: black; }
a:link, a:visited { border-bottom-width: 1px; border-bottom-style: solid; }

@ -0,0 +1,87 @@
/* Solarized Dark
For use with Jekyll and Pygments
http://ethanschoonover.com/solarized
SOLARIZED HEX ROLE
--------- -------- ------------------------------------------
base03 #002b36 background
base01 #586e75 comments / secondary content
base1 #93a1a1 body text / default code / primary content
orange #cb4b16 constants
red #dc322f regex, special keywords
blue #268bd2 reserved keywords
cyan #2aa198 strings, numbers
green #859900 operators, other keywords
*/
.highlight { background-color: #002b36; color: #93a1a1 }
.highlight .c { color: #586e75 } /* Comment */
.highlight .err { color: #93a1a1 } /* Error */
.highlight .g { color: #93a1a1 } /* Generic */
.highlight .k { color: #859900 } /* Keyword */
.highlight .l { color: #93a1a1 } /* Literal */
.highlight .n { color: #93a1a1 } /* Name */
.highlight .o { color: #859900 } /* Operator */
.highlight .x { color: #cb4b16 } /* Other */
.highlight .p { color: #93a1a1 } /* Punctuation */
.highlight .cm { color: #586e75 } /* Comment.Multiline */
.highlight .cp { color: #859900 } /* Comment.Preproc */
.highlight .c1 { color: #586e75 } /* Comment.Single */
.highlight .cs { color: #859900 } /* Comment.Special */
.highlight .gd { color: #2aa198 } /* Generic.Deleted */
.highlight .ge { color: #93a1a1; font-style: italic } /* Generic.Emph */
.highlight .gr { color: #dc322f } /* Generic.Error */
.highlight .gh { color: #cb4b16 } /* Generic.Heading */
.highlight .gi { color: #859900 } /* Generic.Inserted */
.highlight .go { color: #93a1a1 } /* Generic.Output */
.highlight .gp { color: #93a1a1 } /* Generic.Prompt */
.highlight .gs { color: #93a1a1; font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #cb4b16 } /* Generic.Subheading */
.highlight .gt { color: #93a1a1 } /* Generic.Traceback */
.highlight .kc { color: #cb4b16 } /* Keyword.Constant */
.highlight .kd { color: #268bd2 } /* Keyword.Declaration */
.highlight .kn { color: #859900 } /* Keyword.Namespace */
.highlight .kp { color: #859900 } /* Keyword.Pseudo */
.highlight .kr { color: #268bd2 } /* Keyword.Reserved */
.highlight .kt { color: #dc322f } /* Keyword.Type */
.highlight .ld { color: #93a1a1 } /* Literal.Date */
.highlight .m { color: #2aa198 } /* Literal.Number */
.highlight .s { color: #2aa198 } /* Literal.String */
.highlight .na { color: #93a1a1 } /* Name.Attribute */
.highlight .nb { color: #B58900 } /* Name.Builtin */
.highlight .nc { color: #268bd2 } /* Name.Class */
.highlight .no { color: #cb4b16 } /* Name.Constant */
.highlight .nd { color: #268bd2 } /* Name.Decorator */
.highlight .ni { color: #cb4b16 } /* Name.Entity */
.highlight .ne { color: #cb4b16 } /* Name.Exception */
.highlight .nf { color: #268bd2 } /* Name.Function */
.highlight .nl { color: #93a1a1 } /* Name.Label */
.highlight .nn { color: #93a1a1 } /* Name.Namespace */
.highlight .nx { color: #93a1a1 } /* Name.Other */
.highlight .py { color: #93a1a1 } /* Name.Property */
.highlight .nt { color: #268bd2 } /* Name.Tag */
.highlight .nv { color: #268bd2 } /* Name.Variable */
.highlight .ow { color: #859900 } /* Operator.Word */
.highlight .w { color: #93a1a1 } /* Text.Whitespace */
.highlight .mf { color: #2aa198 } /* Literal.Number.Float */
.highlight .mh { color: #2aa198 } /* Literal.Number.Hex */
.highlight .mi { color: #2aa198 } /* Literal.Number.Integer */
.highlight .mo { color: #2aa198 } /* Literal.Number.Oct */
.highlight .sb { color: #586e75 } /* Literal.String.Backtick */
.highlight .sc { color: #2aa198 } /* Literal.String.Char */
.highlight .sd { color: #93a1a1 } /* Literal.String.Doc */
.highlight .s2 { color: #2aa198 } /* Literal.String.Double */
.highlight .se { color: #cb4b16 } /* Literal.String.Escape */
.highlight .sh { color: #93a1a1 } /* Literal.String.Heredoc */
.highlight .si { color: #2aa198 } /* Literal.String.Interpol */
.highlight .sx { color: #2aa198 } /* Literal.String.Other */
.highlight .sr { color: #dc322f } /* Literal.String.Regex */
.highlight .s1 { color: #2aa198 } /* Literal.String.Single */
.highlight .ss { color: #2aa198 } /* Literal.String.Symbol */
.highlight .bp { color: #268bd2 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #268bd2 } /* Name.Variable.Class */
.highlight .vg { color: #268bd2 } /* Name.Variable.Global */
.highlight .vi { color: #268bd2 } /* Name.Variable.Instance */
.highlight .il { color: #2aa198 } /* Literal.Number.Integer.Long */

@ -0,0 +1,342 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
/* Mockingbird Theme by Nevan Scott nevanscott.com */
/* Modified by Jody Frankowski */
/* Modified by ix5 */
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
em {
font-style: italic;
}
strong {
font-weight: bold;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
body {
font-family: Georgia, serif;
font-size: 16px;
line-height: 1.5em;
color: #444;
}
header, #wrapper {
padding: 0 10px;
min-width: 500px;
max-width: 910px;
margin: auto;
}
a {
text-decoration: none;
color: #15A9DB;
}
ul {
list-style: outside disc;
}
ol {
list-style: outside decimal;
}
h1, h2, h3, h4, h5, h6 {
font-family: sans-serif;
font-weight: bold;
}
h1, h2, h3 {
font-size: 1.5em;
line-height: 1em;
margin: 1em 0;
}
img, p, .post > .highlight, .highlighttable, h4, h5, h6 {
margin-top: 1.2em;
}
blockquote {
margin: 1.5em 1.5em 1.5em .75em;
padding-left: .75em;
border-left: 1px solid #EEE;
}
.date {
color: #CCC;
float: left;
clear: both;
width: 130px;
font-size: 1.5em;
line-height: 1em;
margin: 0 20px 1em 0;
}
.info {
margin-top: 1.3em;
font-family: sans-serif;
text-align: right;
color: #BBB;
}
.info a {
color: inherit;
}
.info a.tags {
background: #CCC;
color: #FFF;
display: inline-block;
padding: 0 .3em;
border: 1px transparent solid;
border-radius: 5px;
margin: 0 0 0.3em 0;
}
.info a.tags:hover {
background: inherit;
color: inherit;
}
.info a.tags.selected {
border: 1px #999 solid;
}
.post {
margin: 0 0 4.5em 150px;
}
.post.archives {
margin-bottom: 1.5em;
margin-left: 160px;
}
.post p {
text-align: justify;
}
.page {
margin: 0 90px;
}
.highlight {
border-radius: 3px;
}
.code > .highlight {
border-radius: 0px 3px 3px 0px;
}
.linenos {
border-radius: 3px 0px 0px 3px;
background-color: #073642;
border-right: 1px solid #00232C;
color: #586E75;
text-shadow: 0px -1px #021014;
}
td.code {
width: 100%;
max-width: 100px;
}
.linenos a {
color: #586E75;
}
img {
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.15);
border-radius: 0.3em;
max-width: 100%;
display: block;
margin-left: auto;
margin-right: auto;
}
/*sub and sup stolen from Twitter bootstrap.*/
sub, sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
.post pre, .page pre {
padding: .8em;
font-size: 12px;
font-family: Monospace;
line-height: 1.1em;
overflow: auto;
}
form.inline_edit {
clear: both;
margin: 4.5em 0;
background-color: #DDD;
color: #000;
padding: 20px;
border-radius: 5px;
}
.inline_edit .sub {
color: #888;
white-space: nowrap;
}
.inline_edit label {
float: left;
clear: both;
width: 140px;
margin-right: 20px;
}
.inline_edit .buttons {
display: block;
text-align: right;
}
nav ul {
float: right;
list-style: none;
margin: 0 0 0 3em;
padding: 0;
}
nav li {
float: left;
}
nav a {
display: block;
padding: 4.5em 10px 10px 10px;
}
nav a:hover {
background-color: #d3d3d3;
color: #FFF;
}
nav li.selected a {
background-color: #15A9DB;
color: #FFF;
}
header .header_box {
padding-top: 4.5em;
}
header h1 {
font-size: 1.5em;
line-height: 1em;
margin: 0;
}
header h2 {
font-size: 1em;
margin: .3em 0;
color: #DDD;
}
#content {
margin-top: 3em;
}
.pages {
font-family: sans-serif;
line-height: 2.5em;
margin: 4.5em 0 3em;
background-color: #F9F9F9;
color: #444;
border-radius: 5px;
}
.pages a.next_page {
float: right;
width: 140px;
text-align: center;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
background-color: #EEE;
}
.pages a.prev_page {
float: left;
width: 140px;
text-align: center;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
background-color: #EEE;
}
.pages a {
color: inherit;
border: none;
}
.pages a:hover {
background-color: #DDD;
}
.pages span {
display: block;
margin: 0 160px;
text-align: center;
}
code {
background-color: #F9F2F4;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
box-sizing: border-box;
color: #C7254E;
font-family: Monaco,Menlo,Consolas,"Courier New",monospace;
font-size: 12.6px;
line-height: 18px;
padding-bottom: 2px;
padding-left: 4px;
padding-right: 4px;
padding-top: 2px;
white-space: nowrap;
}
footer {
font-family: sans-serif;
line-height: 2.5em;
text-align: center;
color: #BBB;
margin: 3em 0;
border: 1px solid #EEE;
border-radius: 5px;
}
footer p { margin: 0; }
.right { float: right; }
.clear { clear: both; }

@ -0,0 +1,11 @@
{% if GOOGLE_ANALYTICS %}
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("{{GOOGLE_ANALYTICS}}");
pageTracker._trackPageview();
} catch(err) {}</script>
{% endif %}

@ -0,0 +1,22 @@
{% extends "base.html" %}
{% block title %}{{ SITENAME }} | Archives{% endblock %}
{% block content %}
<h1>Archives</h1>
{# based on http://stackoverflow.com/questions/12764291/jinja2-group-by-month-year #}
{% for year, year_group in dates|groupby('date.year')|reverse %}
{% for month, month_group in year_group|groupby('date.month')|reverse %}
<h4 class="date">{{ (month_group|first).date|strftime('%b %Y') }}</h4>
<div class="post archives">
<ul>
{% for article in month_group %}
<li><a href="{{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></li>
{% endfor %}
</ul>
</div>
{% endfor %}
{% endfor %}
{% endblock %}

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block head %}
{{ super() }}
{% if article.tags %}
<meta name="keywords" content="{{ article.tags|join(",") }}" />
{% endif %}
{% if article.description %}
<meta name="description" content="{{ article.description }}" />
{% endif %}
{% endblock %}
{% block title %}{{ SITENAME }} | {{ article.title }}{% endblock %}
{% block content %}
{% include "article_stub.html" %}
{% endblock %}

@ -0,0 +1,37 @@
{% if not articles_page or first_article_of_day %}
<h4 class="date">{{ article.date.strftime("%b %d, %Y") }}</h4>
{% endif %}
<article class="post">
{% if article.title %}
<h2 class="title">
<a href="{{ SITEURL }}/{{ article.url }}" rel="bookmark" title="Permanent Link to &quot;{{ article.title }}&quot;">{{ article.title }}</a>
</h2>
{% endif %}
{% if not articles_page %}
{% include "translations.html" %}
{% endif %}
{{ article.content }}
<div class="clear"></div>
<div class="info">
<a href="{{ SITEURL }}/{{ article.url }}">posted at {{ article.date.strftime("%H:%M") }}</a>
{% if article.category.name != "misc" %}
&nbsp;&middot;&nbsp;<a href="{{ SITEURL }}/{{ article.category.url }}" rel="tag">{{ article.category }}</a>
{% endif %}
{% if article.tags %}
&nbsp;&middot;
{% for t in article.tags %}
&nbsp;<a href="{{ SITEURL }}/{{ t.url }}" class="tags{% if tag and tag.name == t.name %} selected{% endif %}">{{ t }}</a>
{% endfor %}
{% endif %}
</div>
{% if articles_page and DISQUS_SITENAME %}
<a href="{{ SITEURL }}/{{ article.url }}#disqus_thread">Click to read and post comments</a>
{% else %}
{% include "disqus.html" %}
{% endif %}
</article>

@ -0,0 +1,7 @@
{% extends "index.html" %}
{% block title %}{{ SITENAME }} | Articles by {{ author }}{% endblock %}
{% block ephemeral_nav %}
{{ ephemeral_nav_link(author, output_file, True) }}
{% endblock %}

@ -0,0 +1,105 @@
{% macro ephemeral_nav_link(what, where, selected=False) -%}
<li class="ephemeral{% if selected %} selected{% endif %}"><a href="{{ SITEURL }}/{{ where }}">{{what}}</a></li>
{%- endmacro -%}
<!DOCTYPE html>
<html lang="{{ DEFAULT_LANG }}">
<head>
{% block head %}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{% block title %}{{ SITENAME }}{% endblock title %}</title>
{# favicon #}
<link rel="shortcut icon" type="image/png" href="{{ SITEURL }}/favicon.png">
<link rel="shortcut icon" type="image/x-icon" href="{{ SITEURL }}/favicon.ico">
{% if FEED_ALL_ATOM %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_ALL_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Full Atom Feed" />
{% endif %}
{% if FEED_ALL_RSS %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_ALL_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Full RSS Feed" />
{% endif %}
{% if FEED_ATOM %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Atom Feed" />
{% endif %}
{% if FEED_RSS %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_RSS }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} RSS Feed" />
{% endif %}
{% if CATEGORY_FEED_ATOM and category %}
<link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_ATOM.format(slug=category.slug) }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Categories Atom Feed" />
{% endif %}
{% if CATEGORY_FEED_RSS and category %}
<link href="{{ FEED_DOMAIN }}/{{ CATEGORY_FEED_RSS.format(slug=category.slug) }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Categories RSS Feed" />
{% endif %}
{% if TAG_FEED_ATOM and tag %}
<link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_ATOM.format(slug=tag.slug) }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Tags Atom Feed" />
{% endif %}
{% if TAG_FEED_RSS and tag %}
<link href="{{ FEED_DOMAIN }}/{{ TAG_FEED_RSS.format(slug=tag.slug) }}" type="application/rss+xml" rel="alternate" title="{{ SITENAME }} Tags RSS Feed" />
{% endif %}
<link rel="stylesheet" href="{{ SITEURL }}/theme/css/screen.css" type="text/css" />
<link rel="stylesheet" href="{{ SITEURL }}/theme/css/pygments.css" type="text/css" />
<link rel="stylesheet" href="{{ SITEURL }}/theme/css/print.css" type="text/css" media="print" />
<meta name="generator" content="Pelican" />
<meta name="description" content="{{ SITEDESCRIPTION }}" />
<meta name="author" content="{{ AUTHOR }}" />
{% endblock head %}
</head>
<body>
{% if DISPLAY_HEADER or DISPLAY_HEADER is not defined %}
<header>
{% if DISPLAY_MENU or DISPLAY_MENU is not defined %}
<nav>
<ul>
<!-- {% block ephemeral_nav %}{% endblock %} -->
{% if DISPLAY_HOME or DISPLAY_HOME is not defined %}
<li{% if output_file == "index.html" %} class="selected"{% endif %}><a href="{{ SITEURL }}/">Home</a></li>
{% endif %}
{% if DISPLAY_PAGES_ON_MENU %}
{% for p in pages %}
<li{% if p == page %} class="selected"{% endif %}><a href="{{ SITEURL }}/{{ p.url }}">{{ p.title }}</a></li>
{% endfor %}
{% endif %}
{% for title, link in MENUITEMS %}
<li><a href="{{ link }}">{{ title }}</a></li>
{% endfor %}
{% for name, link, file in MENU_INTERNAL_PAGES %}
<li{% if output_file == file %} class="selected"{% endif %}><a href="{{ SITEURL }}/{{ link }}">{{ name }}</a></li>
{% endfor %}
</ul>
</nav>
{% endif %}
<div class="header_box">
<h1><a href="{{ SITEURL }}/">{{ SITENAME }}</a></h1>
{% if SITESUBTITLE %}
<h2>{{ SITESUBTITLE }}</h2>
{% endif %}
</div>
</header>
{% endif %}
<div id="wrapper">
<div id="content">
{%- block content -%}{%- endblock %}
{% if DISPLAY_FOOTER or DISPLAY_FOOTER is not defined %}
<div class="clear"></div>
<footer>
<p>
<a href="https://github.com/jody-frankowski/blue-penguin">Blue Penguin</a> Theme
&middot;
Powered by <a href="http://getpelican.com">Pelican</a>
{% if FEED_ALL_ATOM %}
&middot;
<a href="{{ SITEURL }}/{{ FEED_ALL_ATOM }}" rel="alternate">Atom Feed</a>
{% endif %}
{% if FEED_ALL_RSS %}
&middot;
<a href="{{ SITEURL }}/{{ FEED_ALL_RSS }}" rel="alternate">Rss Feed</a>
{% endif %}
</footer>
{% endif %}
</div>
<div class="clear"></div>
</div>
{% include 'analytics.html' %}
</body>
</html>

@ -0,0 +1,6 @@
{% extends "index.html" %}
{% block title %}{{ SITENAME }} | articles in the "{{ category }}" category{% if articles_page.number != 0 %} | Page {{ articles_page.number }}{% endif %}{% endblock %}
{% block ephemeral_nav %}
{{ ephemeral_nav_link(category, output_file, True) }}
{% endblock %}

@ -0,0 +1,12 @@
{% if DISQUS_SITENAME %}
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_shortname = '{{ DISQUS_SITENAME }}';
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
{% endif %}

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block title %}{{ SITENAME }}{% if articles_page.number != 1 %} | Page {{ articles_page.number }}{% endif %}{% endblock %}
{% block content %}
{% set date = None %}
{% for article in articles_page.object_list %}
{% if date != article.date.date() %}
{% set first_article_of_day = True %}
{% else %}
{% set first_article_of_day = False %}
{% endif %}
{% set date = article.date.date() %}
{% include "article_stub.html" %}
{% endfor %}
{% include "pagination.html" %}
{% endblock %}

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<div class="page">
{{ page.content }}
</div>
{% endblock %}

@ -0,0 +1,38 @@
{# Use PAGINATION_PATTERNS or pagination may break #}
{% if DEFAULT_PAGINATION and (articles_page.has_previous() or articles_page.has_next()) %}
<div class="clear"></div>
<div class="pages">
{% if PAGINATION_PATTERNS[0][0] != 0 %}
{%- if articles_page.has_previous() %}
{% if articles_page.previous_page_number() == 1 %}
<a href="{{ SITEURL }}/" class="prev_page">&larr;&nbsp;Previous</a>
{%- else %}
<a href="{{ SITEURL }}/page/{{ articles_page.previous_page_number() }}" class="prev_page">&larr;&nbsp;Previous</a>
{%- endif %}
{%- endif %}
{%- if articles_page.has_next() %}
<a href="{{ SITEURL }}/page/{{ articles_page.next_page_number() }}" class="next_page">Next&nbsp;&rarr;</a>
{%- endif %}
{% else %}
{%- if articles_page.has_previous() %}
{% if articles_page.previous_page_number() == 1 %}
<a href="{{ SITEURL }}/{{ page_name }}.html" class="prev_page">&larr;&nbsp;Previous</a>
{%- else %}
<a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.previous_page_number() }}.html" class="prev_page">&larr;&nbsp;Previous</a>
{%- endif %}
{%- endif %}
{%- if articles_page.has_next() %}
<a href="{{ SITEURL }}/{{ page_name }}{{ articles_page.next_page_number() }}.html" class="next_page">Next&nbsp;&rarr;</a>
{%- endif %}
{% endif %}
<span>Page {{ articles_page.number }} of {{ articles_paginator.num_pages }}</span>
</div>
{% endif %}

@ -0,0 +1,5 @@
{% extends "index.html" %}
{% block title %}{{ SITENAME }} | articles tagged "{{ tag }}"{% if articles_page.number != 1 %} | Page {{ articles_page.number }}{% endif %}{% endblock %}
{% block ephemeral_nav %}
{{ ephemeral_nav_link(tag, output_file, True) }}
{% endblock %}

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<ul>
{% for tag, articles in tags %}
<li><a href="{{ SITEURL }}/{{ tag.url }}">{{ tag }}</a></li>
{% endfor %}
</ul>
{% endblock %}

@ -0,0 +1,6 @@
{% if article.translations %}
Translations:
{% for translation in article.translations %}
<a href="{{ SITEURL }}/{{ translation.url }}">{{ translation.lang }}</a>
{% endfor %}
{% endif %}

@ -0,0 +1,59 @@
Title: Releasing 0.5.0
Category: Blog
Date: 2019-03-21 21:30
So `barrel.rs` has a blog now 🎉!
Today we are also happy to announce the release of version `0.5.0`.
A lot of work as gone into this release and breaks the API in a few ways.
This post will quickly highlight new features and also changes to the API.
### New type system
This release adds `barrel::types` which replaces the old `Column` types.
While types are still enums with different type variants, the creation
is now much more streamlined with a builder-style API.
```rust
types::varchar(255)
.nullable(true)
.default("Alice")
```
This change allows us to more easily support database specific types
in the future and makes it easier for you to create your own custom
type builders.
Check out the `barrel::types` documentation for details.
### Explicit IDs for tables
Since `0.2.0` a new table had an implicit `PRIMARY` key called `id`.
It was brought up several times that `barrel` should not have implicit behaviour like this
and and as such we have no reverted this!
Now you will need to create a new `id` field explicitly
```rust
table.add_column("id", types::primary())
```
We're still evaluating the option to add a `table.id()` function
(or similar) to make this process easier.
### Various changes
The `DatabaseExecutor` was renamed to `SqlRunner`,
there were several bug-fixes around `diesel` integration as well as
the table API.
Most importantly, you will need a very recent `diesel` version
which then depends on `0.5.0`.
If you're using barrel currently, nothing will change until you update.
Be aware that your existing migrations will need adjustment if you plan
on re-running them!
---
All in all, this is a pretty big step forward.
We're also tracking some things to implement on a roadmap
for an upcoming `1.0.0` release later this year (maybe!)

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="10cm" height="4.5cm" viewBox="0 0 100 44.999997" version="1.1" id="svg8" inkscape:version="0.92.3 (2405546, 2018-03-11)">
<defs id="defs2"/>
<metadata id="metadata5">
</metadata>
<g inkscape:label="Layer 1" id="layer1" transform="translate(0,-252.00026)">
<g style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20.37609673px;line-height:1.25;font-family:Quicksand;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.50940239" id="text968">
<path d="m 41.584764,271.53284 q 1.445345,0 2.605424,0.70366 1.179097,0.68464 1.844716,1.90177 0.684637,1.19811 0.684637,2.68149 0,1.48338 -0.703655,2.70051 -0.684637,1.21714 -1.901769,1.92079 -1.198114,0.68464 -2.681494,0.68464 -1.48338,0 -2.700512,-0.68464 -1.198115,-0.70365 -1.901769,-1.92079 -0.684637,-1.21713 -0.684637,-2.70051 v -4.46916 q 0,-0.1141 0.01902,-0.17115 v -4.25997 q 0,-0.3233 0.190177,-0.53249 0.209195,-0.2092 0.532496,-0.2092 0.3233,0 0.532495,0.2092 0.209195,0.20919 0.209195,0.53249 v 5.57219 q 0.665619,-0.91285 1.692574,-1.42633 1.026955,-0.5325 2.263105,-0.5325 z m -0.152141,9.26162 q 1.084008,0 1.958822,-0.51348 0.893831,-0.53249 1.388291,-1.42633 0.513478,-0.91284 0.513478,-2.03489 0,-1.12204 -0.513478,-2.01587 -0.49446,-0.91285 -1.388291,-1.42633 -0.874814,-0.51348 -1.958822,-0.51348 -1.502398,0 -2.567389,0.95089 -1.045973,0.95088 -1.236149,2.43426 0,0.0571 -0.01902,0.0951 -0.01902,0.15214 -0.01902,0.47544 0,1.12205 0.49446,2.03489 0.513478,0.89384 1.388292,1.42633 0.874813,0.51348 1.958822,0.51348 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;stroke-width:0.50940239" id="path841"/>
<path d="m 54.327211,271.53284 q 1.48338,0 2.681494,0.70366 1.217132,0.68464 1.901769,1.90177 0.703655,1.21713 0.703655,2.70051 v 4.46916 q 0,0.30428 -0.209195,0.51347 -0.209194,0.2092 -0.532495,0.2092 -0.323301,0 -0.532495,-0.19018 -0.209195,-0.20919 -0.209195,-0.53249 v -1.14106 q -0.665619,0.91284 -1.692574,1.44534 -1.026956,0.51348 -2.263106,0.51348 -1.445344,0 -2.624441,-0.68464 -1.160079,-0.70365 -1.844716,-1.90177 -0.665619,-1.21713 -0.665619,-2.70051 0,-1.48338 0.684637,-2.70051 0.703654,-1.21713 1.901769,-1.90177 1.217132,-0.70366 2.700512,-0.70366 z m 0,9.26162 q 1.084008,0 1.958822,-0.51348 0.874814,-0.51348 1.369274,-1.40731 0.513477,-0.91285 0.513477,-2.03489 0,-1.12204 -0.513477,-2.01588 -0.49446,-0.91284 -1.369274,-1.42632 -0.874814,-0.5325 -1.958822,-0.5325 -1.084008,0 -1.97784,0.5325 -0.874814,0.51348 -1.388291,1.42632 -0.49446,0.89384 -0.49446,2.01588 0,1.12204 0.49446,2.03489 0.513477,0.89383 1.388291,1.40731 0.893832,0.51348 1.97784,0.51348 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;stroke-width:0.50940239" id="path843"/>
<path d="m 67.178119,271.51383 q 0.798743,0 1.255168,0.20919 0.475442,0.2092 0.475442,0.58955 0,0.11411 -0.01902,0.17116 -0.07607,0.26625 -0.24723,0.36133 -0.152141,0.0951 -0.437407,0.0951 -0.171159,0 -0.589548,-0.038 -0.152141,-0.019 -0.456425,-0.019 -1.426326,0 -2.339175,0.76071 -0.893832,0.76071 -0.893832,1.97784 v 5.68629 q 0,0.34232 -0.190177,0.53249 -0.190177,0.19018 -0.532495,0.19018 -0.342319,0 -0.532496,-0.19018 -0.190176,-0.19017 -0.190176,-0.53249 v -8.95734 q 0,-0.34231 0.190176,-0.53249 0.190177,-0.19018 0.532496,-0.19018 0.342318,0 0.532495,0.19018 0.190177,0.19018 0.190177,0.53249 v 0.83678 q 0.551513,-0.79874 1.407309,-1.23615 0.855796,-0.4374 1.844716,-0.4374 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;stroke-width:0.50940239" id="path845"/>
<path d="m 75.90694,271.51383 q 0.798743,0 1.255168,0.20919 0.475442,0.2092 0.475442,0.58955 0,0.11411 -0.01902,0.17116 -0.07607,0.26625 -0.24723,0.36133 -0.152141,0.0951 -0.437407,0.0951 -0.171159,0 -0.589548,-0.038 -0.152142,-0.019 -0.456425,-0.019 -1.426327,0 -2.339176,0.76071 -0.893831,0.76071 -0.893831,1.97784 v 5.68629 q 0,0.34232 -0.190177,0.53249 -0.190177,0.19018 -0.532495,0.19018 -0.342319,0 -0.532496,-0.19018 -0.190177,-0.19017 -0.190177,-0.53249 v -8.95734 q 0,-0.34231 0.190177,-0.53249 0.190177,-0.19018 0.532496,-0.19018 0.342318,0 0.532495,0.19018 0.190177,0.19018 0.190177,0.53249 v 0.83678 q 0.551513,-0.79874 1.407309,-1.23615 0.855796,-0.4374 1.844716,-0.4374 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;stroke-width:0.50940239" id="path847"/>
<path d="m 83.366629,271.53284 q 1.407309,0 2.491318,0.66562 1.084008,0.6466 1.673556,1.80668 0.608567,1.16008 0.608567,2.64346 0,0.30429 -0.190177,0.49446 -0.190177,0.17116 -0.49446,0.17116 h -7.683147 q 0.152141,1.55945 1.217132,2.52935 1.064991,0.96991 2.662477,0.96991 0.684636,0 1.369273,-0.22822 0.703655,-0.24723 1.122044,-0.60856 0.24723,-0.19018 0.513478,-0.19018 0.209194,0 0.418389,0.15214 0.24723,0.22822 0.24723,0.51348 0,0.24723 -0.209195,0.43741 -0.627584,0.53249 -1.597486,0.89383 -0.969902,0.34232 -1.863733,0.34232 -1.521416,0 -2.71953,-0.66562 -1.198115,-0.68464 -1.882752,-1.88275 -0.665619,-1.21714 -0.665619,-2.73855 0,-1.54043 0.627584,-2.73855 0.646602,-1.21713 1.768645,-1.88275 1.141062,-0.68464 2.586406,-0.68464 z m 0,1.31222 q -1.445344,0 -2.396229,0.89384 -0.950884,0.89383 -1.160079,2.35819 h 6.960475 q -0.152142,-1.46436 -1.064991,-2.35819 -0.912849,-0.89384 -2.339176,-0.89384 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;stroke-width:0.50940239" id="path849"/>
<path d="m 92.842788,282.03061 q -1.16008,0 -1.90177,-0.91285 -0.722672,-0.93187 -0.722672,-2.37721 v -10.84009 q 0,-0.3233 0.209195,-0.51347 0.209194,-0.2092 0.513477,-0.2092 0.323301,0 0.513478,0.2092 0.209195,0.19017 0.209195,0.51347 v 10.84009 q 0,0.81776 0.3233,1.33124 0.342319,0.51347 0.855797,0.51347 h 0.475442 q 0.285265,0 0.456424,0.2092 0.17116,0.19018 0.17116,0.51348 0,0.3233 -0.24723,0.53249 -0.228213,0.19018 -0.608567,0.19018 z" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.01769066px;font-family:Comfortaa;stroke-width:0.50940239" id="path851"/>
</g>
<g id="g839" transform="translate(0,0.0917458)">
<rect y="256.67191" x="3.3755143" height="36.679298" width="24.888735" id="rect815-6-7" style="opacity:1;fill:#666666;fill-opacity:1;stroke:none;stroke-width:2.76557231;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;"/>
<rect y="255.34891" x="2.7532961" height="1.5555459" width="26.133173" id="rect815" style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.7789228;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;"/>
<rect y="292.68201" x="2.7532961" height="1.5555459" width="26.133173" id="rect815-3" style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.7789228;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;"/>
<rect y="268.10437" x="3.3755143" height="0.62221843" width="24.888735" id="rect815-5" style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.74082875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;"/>
<rect y="281.17096" x="3.3755143" height="0.62221843" width="24.888735" id="rect815-5-5" style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.74082875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;"/>
<rect y="255.37721" x="16.446571" height="38.852245" width="11.822151" id="rect815-6-8" style="opacity:0.55;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:2.68609262;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;"/>
<rect transform="rotate(-45)" y="200.44443" x="-188.45515" height="11.199931" width="11.199931" id="rect909" style="opacity:1;fill:#e6e6e6;fill-opacity:1;stroke:#868686;stroke-width:0.62221843;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;"/>
<text id="text913" y="276.47345" x="10.648504" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.76389503px;line-height:1.25;font-family:Quicksand;letter-spacing:0px;word-spacing:0px;fill:#202020;fill-opacity:1;stroke:none;stroke-width:0.3190974"><tspan style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.1055584px;font-family:Quicksand;fill:#202020;fill-opacity:1;stroke-width:0.3190974" y="276.47345" x="10.648504" id="tspan911" sodipodi:role="line">SQL</tspan></text>
<rect y="254.88242" x="21.650042" height="1.5555459" width="2.9549315" id="rect815-4" style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.26192206;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;"/>
<rect y="255.37721" x="16.446571" height="38.852242" width="11.822153" id="rect815-6-8-0" style="opacity:0.1;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:2.68609262;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64878049;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

@ -0,0 +1,74 @@
Title: barrel.rs
Subtitle: NULL
URL: /
Save_As: index.html
Template: page
![](images/logo.svg)
A powerful schema migration builder, that let's you write your SQL migrations in Rust.
`barrel` makes writing migrations for different databases as easy as possible.
It provides you with a common API over SQL,
with certain features only provided for database specific implementations.
This way you can focus on your Rust code, and stop worrying about SQL.
## Example
The following example will help you get started
```rust
use barrel::{types, Migration, Pg};
use barrel::backend::Pg;
fn main() {
let mut m = Migration::new();
m.create_table("users", |t| {
t.add_column("name", types::varchar(255));
t.add_column("age", types::integer());
t.add_column("owns_plushy_sharks", types::boolean());
});
println!("{}", m.make::<Pg>());
}
```
## Using Diesel
Since `diesel 1.2.0` it's possible to now use `barrel` for migrations with `diesel`. A guide with some more information on how to get started can be found [here](https://github.com/spacekookie/barrel/blob/master/guides/diesel-setup.md)
### Migration guide
If you've been using `barrel` to write migrations for `diesel` before the `0.5.0` release,
some migration of your migrations will be required.
Since `0.5.0` the way types are constructed changed.
Instead of constructing a type with `Types::VarChar(255)` (an enum variant),
the types are now provided by a module called `types` and builder functions.
The same type would now be `types::varchar(255)` (a function call),
which then returns a `Type` enum.
You can also directly created your own `Type` builders this way.
Check the docs for details!
## Unstable features
Starting with `v0.2.4` `barrel` now has an `unstable` feature flag which will hide features and breaking changes that are in-development at the time of a minor or patch release. You can use these features if you so desire, but be aware that their usage will change more rapidely between versions (even patches) and their usage will be badly documented.
## License
`barrel` is free software: you can redistribute it and/or modify it
under the terms of the MIT Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the MIT Public License for more details.
## Conduct
In the interest of fostering an open and welcoming environment,
the `barrel` project pledges to making participation a harassment-free experience for everyone.
See [Code of Conduct](code_of_conduct.md) for details.
In case of violations, e-mail [kookie@spacekookie.de](mailto:kookie@spacekookie.de).

@ -0,0 +1,103 @@
#!/usr/bin/env bash
##
# This section should match your Makefile
##
PY=${PY:-python}
PELICAN=${PELICAN:-pelican}
PELICANOPTS=
BASEDIR=$(pwd)
INPUTDIR=$BASEDIR/content
OUTPUTDIR=$BASEDIR/output
CONFFILE=$BASEDIR/pelicanconf.py
###
# Don't change stuff below here unless you are sure
###
SRV_PID=$BASEDIR/srv.pid
PELICAN_PID=$BASEDIR/pelican.pid
function usage(){
echo "usage: $0 (stop) (start) (restart) [port]"
echo "This starts Pelican in debug and reload mode and then launches"
echo "an HTTP server to help site development. It doesn't read"
echo "your Pelican settings, so if you edit any paths in your Makefile"
echo "you will need to edit your settings as well."
exit 3
}
function alive() {
kill -0 $1 >/dev/null 2>&1
}
function shut_down(){
PID=$(cat $SRV_PID)
if [[ $? -eq 0 ]]; then
if alive $PID; then
echo "Stopping HTTP server"
kill $PID
else
echo "Stale PID, deleting"
fi
rm $SRV_PID
else
echo "HTTP server PIDFile not found"
fi
PID=$(cat $PELICAN_PID)
if [[ $? -eq 0 ]]; then
if alive $PID; then
echo "Killing Pelican"
kill $PID
else
echo "Stale PID, deleting"
fi
rm $PELICAN_PID
else
echo "Pelican PIDFile not found"
fi
}
function start_up(){
local port=$1
echo "Starting up Pelican and HTTP server"
shift
$PELICAN --debug --autoreload -r $INPUTDIR -o $OUTPUTDIR -s $CONFFILE $PELICANOPTS &
pelican_pid=$!
echo $pelican_pid > $PELICAN_PID
cd $OUTPUTDIR
$PY -m pelican.server $port &
srv_pid=$!
echo $srv_pid > $SRV_PID
cd $BASEDIR
sleep 1
if ! alive $pelican_pid ; then
echo "Pelican didn't start. Is the Pelican package installed?"
return 1
elif ! alive $srv_pid ; then
echo "The HTTP server didn't start. Is there another service using port" $port "?"
return 1
fi
echo 'Pelican and HTTP server processes now running in background.'
}
###
# MAIN
###
[[ ($# -eq 0) || ($# -gt 2) ]] && usage
port=''
[[ $# -eq 2 ]] && port=$2
if [[ $1 == "stop" ]]; then
shut_down
elif [[ $1 == "restart" ]]; then
shut_down
start_up $port
elif [[ $1 == "start" ]]; then
if ! start_up $port; then
shut_down
fi
else
usage
fi

@ -0,0 +1,94 @@
from fabric.api import *
import fabric.contrib.project as project
import os
import shutil
import sys
import SocketServer
from pelican.server import ComplexHTTPRequestHandler
# Local path configuration (can be absolute or relative to fabfile)
env.deploy_path = 'output'
DEPLOY_PATH = env.deploy_path
# Remote server configuration
production = 'spacekookie@lonelyrobot.io:22'
dest_path = '/var/www'
# Rackspace Cloud Files configuration settings
env.cloudfiles_username = 'my_rackspace_username'
env.cloudfiles_api_key = 'my_rackspace_api_key'
env.cloudfiles_container = 'my_cloudfiles_container'
# Github Pages configuration
env.github_pages_branch = "gh-pages"
# Port for `serve`
PORT = 4000
def clean():
"""Remove generated files"""
if os.path.isdir(DEPLOY_PATH):
shutil.rmtree(DEPLOY_PATH)
os.makedirs(DEPLOY_PATH)
def build():
"""Build local version of site"""
local('pelican -s pelicanconf.py')
def rebuild():
"""`clean` then `build`"""
clean()
build()
def regenerate():
"""Automatically regenerate site upon file modification"""
local('pelican -r -s pelicanconf.py')
def serve():
"""Serve site at http://localhost:4000/"""
os.chdir(env.deploy_path)
class AddressReuseTCPServer(SocketServer.TCPServer):
allow_reuse_address = True
server = AddressReuseTCPServer(('', PORT), ComplexHTTPRequestHandler)
sys.stderr.write('Serving on port {0} ...\n'.format(PORT))
server.serve_forever()
def reserve():
"""`build`, then `serve`"""
build()
serve()
def preview():
"""Build production version of site"""
local('pelican -s publishconf.py')
def cf_upload():
"""Publish to Rackspace Cloud Files"""
rebuild()
with lcd(DEPLOY_PATH):
local('swift -v -A https://auth.api.rackspacecloud.com/v1.0 '
'-U {cloudfiles_username} '
'-K {cloudfiles_api_key} '
'upload -c {cloudfiles_container} .'.format(**env))
@hosts(production)
def publish():
"""Publish to production via rsync"""
local('pelican -s publishconf.py')
project.rsync_project(
remote_dir=dest_path,
exclude=".DS_Store",
local_dir=DEPLOY_PATH.rstrip('/') + '/',
delete=True,
extra_opts='-c',
)
def gh_pages():
"""Publish to GitHub Pages"""
rebuild()
local("ghp-import -b {github_pages_branch} {deploy_path}".format(**env))
local("git push origin {github_pages_branch}".format(**env))

@ -0,0 +1,77 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals
AUTHOR = 'Squirrel People'
SITENAME = 'barrel.rs'
SITEURL = 'https://rust-db.github.io/barrel'
THEME = 'blue-penguin'
EXTRA_PATH_METADATA = {
'favicon.ico': {'path': 'favicon.ico'}
}
TEMPLATE_DEBUG = True
DEBUG = True
READ_TIME = 180
PATH = 'content'
STATIC_PATHS = ['images']
SITE_LOGO = 'favicon.ico'
#############################################
#############################################
DEFAULT_CATEGORY = 'Blog'
DEFAULT_DATE = 'fs'
DISPLAY_CATEGORIES_ON_MENU = False
DISPLAY_PAGES_ON_MENU = False
# use those if you want pelican standard pages to appear in your menu
MENU_INTERNAL_PAGES = (
# ('Tags', TAGS_URL, TAGS_SAVE_AS),
# ('Authors', AUTHORS_URL, AUTHORS_SAVE_AS),
('Blog', 'blog/', 'blog/index.html'),
# ('Archives', ARCHIVES_URL, ARCHIVES_SAVE_AS),
)
MENUITEMS = (
)
ARTICLE_URL = '{category}/{slug}'
ARTICLE_SAVE_AS = '{category}/{slug}/index.html'
PAGE_URL = '{slug}'
PAGE_SAVE_AS = '{slug}/index.html'
CATEGORY_URL = '{slug}'
CATEGORY_SAVE_AS = '{slug}/index.html'
TAG_URL = '{slug}'
TAG_SAVE_AS = '{slug}/index.html'
#############################################
#############################################
TIMEZONE = 'UTC'
DEFAULT_LANG = 'en'
LOCALE = 'C'
DEFAULT_DATE_FORMAT = '%Y-%m-%d'
# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None
AUTHOR_FEED_ATOM = None
FEED_RSS = 'rss.xml'
CATEGORY_FEED_RSS = '%s/rss.xml'
JINJA_ENVIRONMENT = {
'extensions': ['webassets.ext.jinja2.AssetsExtension', 'jinja2.ext.with_']
}
# DEFAULT_PAGINATION = 20

@ -0,0 +1,21 @@
extern crate barrel;
use barrel::backend::Pg;
use barrel::types;
use barrel::*;
fn main() {
let mut m = Migration::new();
// A new table is automatically created with an "id" primary key
// To disable that call `without_id` on the return of `create_table`
m.create_table("users", |t: &mut Table| {
t.add_column("name", types::varchar(255)); // Default name is "Anonymous"
t.add_column("description", types::text().nullable(true)); // Can be null
t.add_column("age", types::integer());
t.add_column("posts", types::foreign("posts", vec!["id", "url"]));
t.add_column("owns_plushy_sharks", types::boolean());
});
println!("{}", m.make::<Pg>());
}

@ -0,0 +1,17 @@
use barrel::backend::Sqlite;
use barrel::{types, Migration, Table};
fn main() {
let mut m = Migration::new();
m.create_table("users", |t: &mut Table| {
t.add_column("id", types::text().primary(true));
t.add_column("name", types::varchar(255).default("Anonymous")); // Default name is "Anonymous"
t.add_column("description", types::text().nullable(true)); // Can be null
t.add_column("age", types::integer());
t.add_column("posts", types::foreign("posts", "id"));
t.add_column("created_at", types::date());
t.add_column("owns_plushy_sharks", types::boolean());
});
println!("{}", m.make::<Sqlite>());
}

@ -0,0 +1,17 @@
use barrel::{types, Migration};
fn main() {
let mut m = Migration::new();
m.create_table("users", |t| {
t.add_column("first_name", types::varchar(64).nullable(false));
t.add_column("last_name", types::varchar(64).nullable(false));
t.add_column("birthday", types::date().nullable(false));
t.add_index(
"names",
types::index(vec!["first_name", "last_name"])
.unique(true)
.nullable(false),
);
});
}

@ -0,0 +1,48 @@
# Diesel setup
### Disclaimer
> The barrel crate is still in an early state of development and should not be considered "stable".
>
> Old migrations might break because the API was changed.
> Features might be removed or replaced! **Before 1.0 this crate is not stable
> and should __not__ be used in production**
> – just wanted to make that loud and clear :)
---
Using rust migrations (via `barrel`) with `diesel` is really simple.
First make sure that you installed the `diesel_cli` with the `barrel-migrations` feature flag:
```bash
~ cargo install diesel_cli --features="barrel-migrations"
```
**Important:** you can only select one (1) backend with diesel.
Whichever you select will determine the migration files that are generated later.
```toml
[dependencies]
diesel = { version = "1.3", features = ["sqlite3"] }
# ...
```
From this point using `diesel` is very similar to how you normally use it. The only difference is that you should provide a `--format` flag when letting diesel generate a migration for you. Running migrations doesn't change.
```bash
~ diesel migration generate <name> --format="barrel"
~ diesel migration run
```
A migration file generated by diesel will look as follows
```rust
/// Handle up migrations
fn up(migr: &mut Migration) {}
/// Handle down migrations
fn down(migr: &mut Migration) {}
```
The object provided as a function parameter is a mutable `Migration` object which you can operate on.
Please refer to [the docs](https://docs.rs/barrel/0.2.0/barrel/migration/struct.Migration.html) for API specifics.

@ -0,0 +1,8 @@
with import <nixpkgs> {};
stdenv.mkDerivation {
name = "qaul";
buildInputs = with pkgs; [
rustracer rustup clangStdenv
];
}

@ -0,0 +1,42 @@
The following file outlines some of the changes that should be made to
barrel, before we can promote a version to 1.0. This is in no way an
exhausive list, but certainly a good starting point for contributors
to work with. There are several sections of issues, some of which
were already reported on the repo, others have not.
== Bugs
- https://github.com/rust-db/barrel/issues/79
- https://github.com/rust-db/barrel/issues/80
- https://github.com/rust-db/barrel/issues/81
- https://github.com/rust-db/barrel/issues/82
- https://github.com/rust-db/barrel/issues/83
== Features
- Tests should run against a database backend, not just a static
string. This way we can also enforce compatibility with a specific
database version.
-
- *Testing*: The tests should run against an actual database to make
sure that the syntax is valid. We still run into syntax issues from time to
time. As also mentioned here:
https://github.com/rust-db/barrel/issues/8#issue-297373013
- *Integrate Fork*: One of our freelancers was working with barrel for
some time and eventually had to create a fork in order to do fixes.
Currently we are using his fork but I would really like to switch back to
mainline barrel asap.
- Merge his PRs if possible:
https://github.com/rust-db/barrel/pulls/aknuds1
- There are probably other fixes he has not opened a PR for. Those
should be ported as well. Maybe you can scan his history to see what is
needed. https://github.com/aknuds1/barrel/commits/master

@ -0,0 +1,89 @@
//! A backend module which provides a few generic traits
//! to implement SQL generation for different databases.
//!
//! It also re-exports the generators for existing databases
//! so they can be used more conveniently.
#[cfg(feature = "mysql")]
mod mysql;
#[cfg(feature = "mysql")]
pub use self::mysql::MySql;
#[cfg(feature = "pg")]
mod pg;
#[cfg(feature = "pg")]
pub use self::pg::Pg;
#[cfg(feature = "sqlite3")]
mod sqlite3;
#[cfg(feature = "sqlite3")]
pub use self::sqlite3::Sqlite;
#[allow(unused_imports)]
use crate::{types::Type, Migration};
/// An enum describing all supported Sql flavours
#[derive(Copy, Clone, Debug)]
pub enum SqlVariant {
#[cfg(feature = "sqlite3")]
Sqlite,
#[cfg(feature = "pg")]
Pg,
#[cfg(feature = "mysql")]
Mysql,
#[doc(hidden)]
__Empty,
}
impl SqlVariant {
pub(crate) fn run_for(self, _migr: &Migration) -> String {
match self {
#[cfg(feature = "sqlite3")]
SqlVariant::Sqlite => _migr.make::<Sqlite>(),
#[cfg(feature = "pg")]
SqlVariant::Pg => _migr.make::<Pg>(),
#[cfg(feature = "mysql")]
SqlVariant::Mysql => _migr.make::<MySql>(),
_ => panic!("You need to select an Sql variant!"),
}
}
}
/// A generic SQL generator trait
pub trait SqlGenerator {
/// Create a new table with a name
fn create_table(name: &str, schema: Option<&str>) -> String;
/// Create a new table with a name, only if it doesn't exist
fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String;
/// Drop a table with a name
fn drop_table(name: &str, schema: Option<&str>) -> String;
/// Drop a table with a name, only if it exists
fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String;
/// Rename a table from <old> to <new>
fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String;
/// Modify a table in some other way
fn alter_table(name: &str, schema: Option<&str>) -> String;
/// Create a new column with a type
fn add_column(ex: bool, schema: Option<&str>, name: &str, _type: &Type) -> String;
/// Drop an existing column from the table
fn drop_column(name: &str) -> String;
/// Rename an existing column
fn rename_column(old: &str, new: &str) -> String;
/// Create a multi-column index
fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String;
/// Drop a multi-column index
fn drop_index(name: &str) -> String;
}

@ -0,0 +1,162 @@
//! MySQL implementation of a generator
//!
//! This module generates strings that are specific to MySQL
//! databases. They should be thoroughly tested via unit testing
use super::SqlGenerator;
use crate::types::{BaseType, Type};
/// A simple macro that will generate a schema prefix if it exists
macro_rules! prefix {
($schema:expr) => {
$schema
.map(|s| format!("`{}`.", s))
.unwrap_or_else(|| String::new())
};
}
/// MySQL generator backend
pub struct MySql;
impl SqlGenerator for MySql {
fn create_table(name: &str, schema: Option<&str>) -> String {
format!("CREATE TABLE {}`{}`", prefix!(schema), name)
}
fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String {
format!("CREATE TABLE {}`{}` IF NOT EXISTS", prefix!(schema), name)
}
fn drop_table(name: &str, schema: Option<&str>) -> String {
format!("DROP TABLE {}`{}`", prefix!(schema), name)
}
fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String {
format!("DROP TABLE {}`{}` IF EXISTS", prefix!(schema), name)
}
fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String {
let schema = prefix!(schema);
format!("RENAME TABLE {}`{}` TO {}`{}`", schema, old, schema, new)
}
fn alter_table(name: &str, schema: Option<&str>) -> String {
format!("ALTER TABLE {}`{}`", prefix!(schema), name)
}
fn add_column(ex: bool, schema: Option<&str>, name: &str, tt: &Type) -> String {
let bt: BaseType = tt.get_inner();
use self::BaseType::*;
let name = format!("`{}`", name);
#[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */
format!(
"{}{}{}{}{}",
match bt {
Text => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Varchar(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Primary => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Integer => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Float => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Double => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
UUID => unimplemented!(),
Json => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Boolean => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Date => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Binary => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Foreign(_, _, _) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Custom(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Array(it) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(Array(Box::new(*it)), schema)),
Index(_) => unreachable!(),
},
match tt.primary {
true => " PRIMARY KEY",
false => "",
},
match (&tt.default).as_ref() {
Some(ref m) => format!(" DEFAULT '{}'", m),
_ => format!(""),
},
match tt.nullable {
true => "",
false => " NOT NULL",
},
match tt.unique {
true => " UNIQUE",
false => "",
},
)
}
fn drop_column(name: &str) -> String {
format!("DROP COLUMN `{}`", name)
}
fn rename_column(old: &str, new: &str) -> String {
format!("CHANGE COLUMN `{}` `{}`", old, new)
}
fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String {
// FIXME: Implement Mysql specific index builder here
format!(
"CREATE {} INDEX `{}` ON {}`{}` ({})",
match _type.unique {
true => "UNIQUE",
false => "",
},
name,
prefix!(schema),
table,
match _type.inner {
BaseType::Index(ref cols) => cols
.iter()
.map(|col| format!("`{}`", col))
.collect::<Vec<_>>()
.join(", "),
_ => unreachable!(),
}
)
}
fn drop_index(name: &str) -> String {
format!("DROP INDEX `{}`", name)
}
}
impl MySql {
fn prefix(ex: bool) -> String {
match ex {
true => format!("ADD COLUMN "),
false => format!(""),
}
}
fn print_type(t: BaseType, schema: Option<&str>) -> String {
use self::BaseType::*;
match t {
Text => format!("TEXT"),
Varchar(l) => match l {
0 => format!("VARCHAR"), // For "0" remove the limit
_ => format!("VARCHAR({})", l),
},
/* "NOT NULL" is added here because normally primary keys are implicitly not-null */
Primary => format!("INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY"),
Integer => format!("INTEGER"),
Float => format!("FLOAT"),
Double => format!("DOUBLE"),
UUID => format!("CHAR(36)"),
Boolean => format!("BOOLEAN"),
Date => format!("DATE"),
Json => format!("JSON"),
Binary => format!("BYTEA"),
Foreign(s, t, refs) => format!(
"INTEGER REFERENCES {}{}({})",
prefix!(s),
t,
refs.0.join(",")
),
Custom(t) => format!("{}", t),
Array(meh) => format!("{}[]", MySql::print_type(*meh, schema)),
Index(_) => unreachable!(),
}
}
}

@ -0,0 +1,164 @@
//! Postgres implementation of a generator
//!
//! This module generates strings that are specific to Postgres
//! databases. They should be thoroughly tested via unit testing
use super::SqlGenerator;
use crate::types::{BaseType, Type};
/// A simple macro that will generate a schema prefix if it exists
macro_rules! prefix {
($schema:expr) => {
$schema
.map(|s| format!("\"{}\".", s))
.unwrap_or_else(|| String::new())
};
}
/// Postgres SQL generator backend
pub struct Pg;
impl SqlGenerator for Pg {
fn create_table(name: &str, schema: Option<&str>) -> String {
format!("CREATE TABLE {}\"{}\"", prefix!(schema), name)
}
fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String {
format!("CREATE TABLE IF NOT EXISTS {}\"{}\"", prefix!(schema), name)
}
fn drop_table(name: &str, schema: Option<&str>) -> String {
format!("DROP TABLE {}\"{}\"", prefix!(schema), name)
}
fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String {
format!("DROP TABLE IF EXISTS {}\"{}\"", prefix!(schema), name)
}
fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String {
let schema = prefix!(schema);
format!(
"ALTER TABLE {}\"{}\" RENAME TO {}\"{}\"",
schema, old, schema, new
)
}
fn alter_table(name: &str, schema: Option<&str>) -> String {
format!("ALTER TABLE {}\"{}\"", prefix!(schema), name)
}
fn add_column(ex: bool, schema: Option<&str>, name: &str, tt: &Type) -> String {
let bt: BaseType = tt.get_inner();
use self::BaseType::*;
#[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */
format!(
"{}{}{}{}{}",
match bt {
Text => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Varchar(_) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Primary => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Integer => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Float => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Double => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
UUID => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Json => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Boolean => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Date => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Binary => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Foreign(_, _, _) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Custom(_) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Array(it) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(Array(Box::new(*it)), schema)),
Index(_) => unreachable!(), // Indices are handled via custom builder
},
match tt.primary {
true => " PRIMARY KEY",
false => "",
},
match (&tt.default).as_ref() {
Some(ref m) => format!(" DEFAULT '{}'", m),
_ => format!(""),
},
match tt.nullable {
true => "",
false => " NOT NULL",
},
match tt.unique {
true => " UNIQUE",
false => "",
},
)
}
fn drop_column(name: &str) -> String {
format!("DROP COLUMN \"{}\"", name)
}
fn rename_column(old: &str, new: &str) -> String {
format!("ALTER COLUMN \"{}\" RENAME TO \"{}\"", old, new)
}
fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String {
// FIXME: Implement PG specific index builder here
format!(
"CREATE {} INDEX \"{}\" ON {}\"{}\" ({})",
match _type.unique {
true => "UNIQUE",
false => "",
},
name,
prefix!(schema),
table,
match _type.inner {
BaseType::Index(ref cols) => cols
.iter()
.map(|col| format!("\"{}\"", col))
.collect::<Vec<_>>()
.join(", "),
_ => unreachable!(),
}
)
}
fn drop_index(name: &str) -> String {
format!("DROP INDEX \"{}\"", name)
}
}
impl Pg {
fn prefix(ex: bool) -> String {
match ex {
true => format!("ADD COLUMN "),
false => format!(""),
}
}
fn print_type(t: BaseType, schema: Option<&str>) -> String {
use self::BaseType::*;
match t {
Text => format!("TEXT"),
Varchar(l) => match l {
0 => format!("VARCHAR"), // For "0" remove the limit
_ => format!("VARCHAR({})", l),
},
/* "NOT NULL" is added here because normally primary keys are implicitly not-null */
Primary => format!("SERIAL PRIMARY KEY NOT NULL"),
Integer => format!("INTEGER"),
Float => format!("FLOAT"),
Double => format!("DOUBLE PRECISION"),
UUID => format!("UUID"),
Boolean => format!("BOOLEAN"),
Date => format!("DATE"),
Json => format!("JSON"),
Binary => format!("BYTEA"),
Foreign(s, t, refs) => format!(
"INTEGER REFERENCES {}\"{}\"({})",
prefix!(s.or(schema.map(|s| s.into()))),
t,
refs.0.join(",")
),
Custom(t) => format!("{}", t),
Array(meh) => format!("{}[]", Pg::print_type(*meh, schema)),
Index(_) => unreachable!(), // Indices are handled via custom builder
}
}
}

@ -0,0 +1,155 @@
//! Sqlite3 implementation of a generator
use super::SqlGenerator;
use crate::types::{BaseType, Type};
/// A simple macro that will generate a schema prefix if it exists
macro_rules! prefix {
($schema:expr) => {
$schema
.map(|s| format!("\"{}\".", s))
.unwrap_or_else(|| String::new())
};
}
/// We call this struct Sqlite instead of Sqlite3 because we hope not
/// to have to break the API further down the road
pub struct Sqlite;
impl SqlGenerator for Sqlite {
fn create_table(name: &str, schema: Option<&str>) -> String {
format!("CREATE TABLE {}\"{}\"", prefix!(schema), name)
}
fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String {
format!("CREATE TABLE IF NOT EXISTS {}\"{}\"", prefix!(schema), name)
}
fn drop_table(name: &str, schema: Option<&str>) -> String {
format!("DROP TABLE {}\"{}\"", prefix!(schema), name)
}
fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String {
format!("DROP TABLE IF EXISTS {}\"{}\"", prefix!(schema), name)
}
fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String {
let schema = prefix!(schema);
format!("ALTER TABLE {}\"{}\" RENAME TO \"{}\"", schema, old, new)
}
fn alter_table(name: &str, schema: Option<&str>) -> String {
format!("ALTER TABLE {}\"{}\"", prefix!(schema), name)
}
fn add_column(ex: bool, _: Option<&str>, name: &str, tt: &Type) -> String {
let bt: BaseType = tt.get_inner();
use self::BaseType::*;
#[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */
format!(
// SQL base - default - nullable - unique
"{}{}{}{}{}",
match bt {
Text => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Varchar(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Primary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Integer => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Float => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Double => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
UUID => panic!("`UUID` not supported by Sqlite3. Use `Text` instead!"),
Json => panic!("`Json` not supported by Sqlite3. Use `Text` instead!"),
Boolean => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Date => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Binary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Foreign(_, _, _) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Custom(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
Array(it) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(Array(Box::new(*it)))),
Index(_) => unreachable!(), // Indices are handled via custom builders
},
match tt.primary {
true => " PRIMARY KEY",
false => "",
},
match (&tt.default).as_ref() {
Some(ref m) => format!(" DEFAULT '{}'", m),
_ => format!(""),
},
match tt.nullable {
true => "",
false => " NOT NULL",
},
match tt.unique {
true => " UNIQUE",
false => "",
}
)
}
/// Create a multi-column index
fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String {
format!(
"CREATE {} INDEX {}\"{}\" ON \"{}\" ({});",
match _type.unique {
true => "UNIQUE",
false => "",
},
prefix!(schema),
name,
table,
match _type.inner {
BaseType::Index(ref cols) => cols
.iter()
.map(|col| format!("\"{}\"", col))
.collect::<Vec<_>>()
.join(", "),
_ => unreachable!(),
}
)
}
/// Drop a multi-column index
fn drop_index(name: &str) -> String {
format!("DROP INDEX \"{}\"", name)
}
fn drop_column(_: &str) -> String {
panic!("Sqlite does not support dropping columns!")
}
fn rename_column(_: &str, _: &str) -> String {
panic!("Sqlite does not support renaming columns!")
}
}
impl Sqlite {
fn prefix(ex: bool) -> String {
match ex {
true => format!("ADD COLUMN "),
false => format!(""),
}
}
fn print_type(t: BaseType) -> String {
use self::BaseType::*;
match t {
Text => format!("TEXT"),
Varchar(l) => match l {
0 => format!("VARCHAR"), // For "0" remove the limit
_ => format!("VARCHAR({})", l),
},
Primary => format!("INTEGER NOT NULL PRIMARY KEY"),
Integer => format!("INTEGER"),
Float => format!("REAL"),
Double => format!("DOUBLE"),
UUID => unimplemented!(),
Boolean => format!("BOOLEAN"),
Date => format!("DATE"),
Json => panic!("Json is not supported by Sqlite3"),
Binary => format!("BINARY"),
Foreign(_, t, refs) => format!("INTEGER REFERENCES {}({})", t, refs.0.join(",")),
Custom(t) => format!("{}", t),
Array(meh) => format!("{}[]", Sqlite::print_type(*meh)),
Index(_) => unimplemented!(),
}
}
}

@ -0,0 +1,19 @@
//! A module meant for library developers
//!
//! `barrel` can be used with different migration toolkits or
//! SQL adapters. You can either use it to just generate strings
//! or implemented the provided trait that will then automatically
//! execute the SQL string on your apropriate database backend.
//!
//! You can then simple call `Migration::execute` to run the provided
//! migration.
/// A generic trait that frameworks using barrel can implement
///
/// An object of this trait can be given to a `Migration` object to
/// automatically generate and run the given SQL string for a
/// database connection which is wrapped by it
pub trait SqlRunner {
/// Execute the migration on a backend
fn execute<S: Into<String>>(&mut self, sql: S);
}

@ -0,0 +1,197 @@
//!
// This integration relies on _knowing_ which backend is being used at compile-time
// This is a poor woman's XOR - if you know how to make it more pretty, PRs welcome <3
#[cfg(any(
all(feature = "pg", feature = "mysql"),
all(feature = "pg", feature = "sqlite3"),
all(feature = "mysql", feature = "sqlite3")
))]
compile_error!("`barrel` can only integrate with `diesel` if you select one (1) backend!");
use diesel_rs::connection::SimpleConnection;
use diesel_rs::migration::{Migration, RunMigrationsError};
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;
/// Represents a migration run inside Diesel
///
/// 1. Path
/// 2. Version
/// 3. Up
/// 4. Down
pub struct BarrelMigration(PathBuf, String, String, String);
impl Migration for BarrelMigration {
fn file_path(&self) -> Option<&Path> {
Some(self.0.as_path())
}
fn version(&self) -> &str {
&self.1
}
fn run(&self, conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
conn.batch_execute(&self.2)?;
Ok(())
}
fn revert(&self, conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
conn.batch_execute(&self.3)?;
Ok(())
}
}
/// Generate migration files using the barrel schema builder
pub fn generate_initial(path: &PathBuf) {
generate_initial_with_content(
path,
&"fn up(migr: &mut Migration) {} \n\n".to_string(),
&"fn down(migr: &mut Migration) {} \n".to_string(),
)
}
/// Generate migration files using the barrel schema builder with initial content
pub fn generate_initial_with_content(path: &PathBuf, up_content: &String, down_content: &String) {
let migr_path = path.join("mod.rs");
println!("Creating {}", migr_path.display());
let mut barrel_migr = fs::File::create(migr_path).unwrap();
barrel_migr.write(b"/// Handle up migrations \n").unwrap();
barrel_migr.write(up_content.as_bytes()).unwrap();
barrel_migr.write(b"/// Handle down migrations \n").unwrap();
barrel_migr.write(down_content.as_bytes()).unwrap();
}
/// Generate a Migration from the provided path
pub fn migration_from(path: &Path) -> Option<Box<Migration>> {
match path.join("mod.rs").exists() {
true => Some(run_barrel_migration_wrapper(&path.join("mod.rs"))),
false => None,
}
}
fn version_from_path(path: &Path) -> Result<String, ()> {
path.parent()
.unwrap_or_else(|| {
panic!(
"Migration doesn't appear to be in a directory: `{:?}`",
path
)
})
.file_name()
.unwrap_or_else(|| panic!("Can't get file name from path `{:?}`", path))
.to_string_lossy()
.split('_')
.nth(0)
.map(|s| Ok(s.replace('-', "")))
.unwrap_or_else(|| Err(()))
}
fn run_barrel_migration_wrapper(path: &Path) -> Box<Migration> {
let (up, down) = run_barrel_migration(&path);
let version = version_from_path(path).unwrap();
let migration_path = match path.parent() {
Some(parent_path) => parent_path.to_path_buf(),
None => path.to_path_buf(),
};
Box::new(BarrelMigration(migration_path, version, up, down))
}
fn run_barrel_migration(migration: &Path) -> (String, String) {
/* Create a tmp dir with src/ child */
use tempfile::Builder;
let dir = Builder::new().prefix("barrel").tempdir().unwrap();
fs::create_dir_all(&dir.path().join("src")).unwrap();
let (feat, ident) = get_backend_pair();
let toml = format!(
"# This file is auto generated by barrel
[package]
name = \"tmp-generator\"
description = \"Doing nasty things with cargo\"
version = \"0.0.0\"
authors = [\"Katharina Fey <kookie@spacekookie.de>\"]
# TODO: Use same `barrel` dependency as crate
[dependencies]
barrel = {{ version = \"*\", features = [ {:?} ] }}",
feat
);
/* Add a Cargo.toml file */
let ct = dir.path().join("Cargo.toml");
let mut cargo_toml = File::create(&ct).unwrap();
cargo_toml.write_all(toml.as_bytes()).unwrap();
/* Generate main.rs based on user migration */
let main_file_path = &dir.path().join("src").join("main.rs");
let mut main_file = File::create(&main_file_path).unwrap();
let user_migration = migration.as_os_str().to_os_string().into_string().unwrap();
main_file
.write_all(
format!(
"//! This file is auto generated by barrel
extern crate barrel;
use barrel::*;
use barrel::backend::{ident};
include!(\"{}\");
fn main() {{
let mut m_up = Migration::new();
up(&mut m_up);
println!(\"{{}}\", m_up.make::<{ident}>());
let mut m_down = Migration::new();
down(&mut m_down);
println!(\"{{}}\", m_down.make::<{ident}>());
}}
",
user_migration,
ident = ident
)
.as_bytes(),
)
.unwrap();
let output = if cfg!(target_os = "windows") {
Command::new("cargo")
.current_dir(dir.path())
.arg("run")
.output()
.expect("failed to execute cargo!")
} else {
Command::new("sh")
.current_dir(dir.path())
.arg("-c")
.arg("cargo run")
.output()
.expect("failed to execute cargo!")
};
let output = String::from_utf8_lossy(&output.stdout);
let vec: Vec<&str> = output.split("\n").collect();
let up = String::from(vec[0]);
let down = String::from(vec[1]);
(up, down)
}
/// Uses the fact that barrel with diesel support is only compiled with _one_ feature
///
/// The first string is the feature-name, the other the struct ident
fn get_backend_pair() -> (&'static str, &'static str) {
#[cfg(feature = "pg")]
return ("pg", "Pg");
#[cfg(feature = "mysql")]
return ("mysql", "Mysql");
#[cfg(feature = "sqlite3")]
return ("sqlite3", "Sqlite");
}

@ -0,0 +1,6 @@
//! Include external integrations into frameworks and libraries
//!
//!
#[cfg(feature = "diesel")]
pub mod diesel;

@ -0,0 +1,176 @@
//! Powerful schema migration builder, that let's you write your SQL
//! migrations in Rust.
//!
//! `barrel` makes writing migrations for different databases as easy
//! as possible. It provides you with a common API over SQL, with
//! certain features only provided for database specific
//! implementations. This way you can focus on your Rust code, and
//! stop worrying about SQL.
//!
//! `barrel` has three primary models: the
//! [Migration](migration/struct.Migration.html) which represents all
//! changes and changes made on a database level, the
//! [Table](table/struct.Table.html) and the
//! [Type](types/struct.Type.html).
//!
//! When creating or altering tables a lambda which exposes `&mut
//! Table` is provided for initialisation. Adding columns is then as
//! easy as calling `add_column(...)` on the table.
//!
//! Each column is statically typed and some types require some
//! metadata in order to compile the migration (for example
//! `Varchar(255)`). You can also provide default types and override
//! encodings, nullability or uniqueness of columns. Some checks are
//! performed at compile-time however most things (including) correct
//! default values) are only checked at runtime.
//!
//! **Note** Since version `0.3.0` it is required to provide a
//! database backend in order to compile `barrel`.
//!
//! The following code is a simple example of how to get going with
//! `barrel`
//!
//! ```rust
//! use barrel::{types, Migration};
//!
//! fn main() {
//! let mut m = Migration::new();
//! m.create_table("users", |t| {
//! t.add_column("name", types::varchar(255));
//! t.add_column("age", types::integer());
//! t.add_column("owns_plushy_sharks", types::boolean());
//! });
//! }
//! ```
//!
//! `barrel` also supports more advanced types, such as `foreign(...)`
//! and `array(...)` however currently doesn't support nested Array
//! types on foreign keys (such as `array(array(foreign(...)))`). Each
//! column addition returns a Column object which can then be used to
//! provide further configuration.
//!
//! To generate SQL strings you have two options. If you just want to
//! run the migration yourself simply run `Migration::exec()` where
//! you provide a generic `SqlGenerator` type according to your
//! database backend
//!
//! ```rust
//! # #[cfg(feature = "pg")]
//! # use barrel::backend::Pg;
//! # use barrel::Migration;
//! # let mut m = Migration::new();
//! // Example for pgsql
//! # #[cfg(feature = "pg")]
//! m.make::<Pg>();
//! ```
//!
//! Alternatively, if you're a library developer and you want to more
//! easily embed `barrel` into your library you can simply implement
//! the `DatabaseExecutor` trait for a type of yours that knows how to
//! execute SQL. Running a migration with `barrel` is then super
//! easy.
//!
//! ```rust
//! use barrel::connectors::SqlRunner;
//! # use barrel::Migration;
//! # #[cfg(feature = "pg")]
//! # use barrel::backend::Pg;
//!
//! struct MyRunner;
//! impl SqlRunner for MyRunner {
//! fn execute<S: Into<String>>(&mut self, sql: S) {
//! # let s: String = sql.into();
//! // ...
//! }
//! }
//!
//! # let mut m = Migration::new();
//! # let mut executor = MyRunner;
//! # #[cfg(feature = "pg")]
//! m.execute::<Pg, _>(&mut executor);
//! ```
//!
//! In this case `executor` is your provided type which implements the
//! required trait. You can read more about this in the
//! [connectors](connectors/index.html) module docs.
//!
//! If you find database-specific features or documentation lacking,
//! don't hesitate to open an issue/PR about it.
#[cfg(feature = "diesel")]
pub mod integrations;
#[cfg(feature = "diesel")]
pub use integrations::*;
pub mod backend;
pub mod connectors;
pub mod migration;
pub mod table;
pub mod types;
pub use backend::SqlVariant;
pub use migration::Migration;
pub use table::{Table, TableMeta};
#[cfg(test)]
mod tests;
use std::rc::Rc;
/// An enum set that represents a single change on a table
#[derive(Clone)]
pub enum TableChange {
/// Add a column of a name and type
AddColumn(String, types::Type),
/// Change an existing column
ChangeColumn(String, types::Type, Rc<dyn Fn(&mut types::Type)>),
/// Simply rename a column
RenameColumn(String, String),
/// Remove a column
DropColumn(String),
/// Add some custom SQL if all else fails
CustomLine(String),
}
/// An enum set that represents a single change on a database
#[derive(Clone)]
pub enum DatabaseChange {
/// Create a new table
CreateTable(Table, Rc<dyn Fn(&mut Table)>),
/// Create a new table *only* if it doesn't exist yet
CreateTableIfNotExists(Table, Rc<dyn Fn(&mut Table)>),
/// Change fields on an existing table
ChangeTable(Table, Rc<dyn Fn(&mut Table)>),
/// Rename a table
RenameTable(String, String),
/// Drop an existing table
DropTable(String),
/// Only drop a table if it exists
DropTableIfExists(String),
/// Add some custom SQL if all else fails
CustomLine(String),
}
/// An enum set that represents operations done with and on indices
#[derive(Clone)]
pub enum IndexChange {
/// Add a multi-column index
AddIndex {
index: String,
table: String,
columns: types::Type, // Should always be a `Index` type
},
/// Remove a multi-column index
RemoveIndex(String, String),
}

@ -0,0 +1,221 @@
//! Core migration creation handler
//!
//! A migration can be done for a specific schema which contains
//! multiple additions or removables from a database or table.
//!
//! At the end of crafting a migration you can use `Migration::exec` to
//! get the raw SQL string for a database backend or `Migration::revert`
//! to try to auto-infer the migration rollback. In cases where that
//! can't be done the `Result<String, RevertError>` will not unwrap.
//!
//! You can also use `Migration::exec` with your SQL connection for convenience
//! if you're a library developer.
use crate::table::{Table, TableMeta};
use crate::DatabaseChange;
use crate::backend::{SqlGenerator, SqlVariant};
use crate::connectors::SqlRunner;
use std::rc::Rc;
/// Represents a schema migration on a database
pub struct Migration {
#[doc(hidden)]
pub schema: Option<String>,
#[doc(hidden)]
pub changes: Vec<DatabaseChange>,
}
impl Migration {
pub fn new() -> Migration {
Migration {
schema: None,
changes: Vec::new(),
}
}
/// Specify a database schema name for this migration
pub fn schema<S: Into<String>>(self, schema: S) -> Migration {
Self {
schema: Some(schema.into()),
..self
}
}
/// Creates the SQL for this migration for a specific backend
///
/// This function copies state and does not touch the original
/// migration layout. This allows you to call `revert` later on
/// in the process to auto-infer the down-behaviour
pub fn make<T: SqlGenerator>(&self) -> String {
use DatabaseChange::*;
/* What happens in make, stays in make (sort of) */
let mut changes = self.changes.clone();
let schema = self.schema.as_ref().map(|s| s.as_str());
changes.iter_mut().fold(String::new(), |mut sql, change| {
match change {
&mut CreateTable(ref mut t, ref mut cb)
| &mut CreateTableIfNotExists(ref mut t, ref mut cb) => {
cb(t); // Run the user code
let (cols, indices) = t.make::<T>(false, schema);
let name = t.meta.name().clone();
sql.push_str(&match change {
CreateTable(_, _) => T::create_table(&name, schema),
CreateTableIfNotExists(_, _) => {
T::create_table_if_not_exists(&name, schema)
}
_ => unreachable!(),
});
sql.push_str(" (");
let l = cols.len();
for (i, slice) in cols.iter().enumerate() {
sql.push_str(slice);
if i < l - 1 {
sql.push_str(", ");
}
}
sql.push_str(")");
// Add additional index columns
if indices.len() > 0 {
sql.push_str(";");
sql.push_str(&indices.join(";"));
}
}
&mut DropTable(ref name) => sql.push_str(&T::drop_table(name, schema)),
&mut DropTableIfExists(ref name) => {
sql.push_str(&T::drop_table_if_exists(name, schema))
}
&mut RenameTable(ref old, ref new) => {
sql.push_str(&T::rename_table(old, new, schema))
}
&mut ChangeTable(ref mut t, ref mut cb) => {
cb(t);
let (cols, indices) = t.make::<T>(true, schema);
sql.push_str(&T::alter_table(&t.meta.name(), schema));
sql.push_str(" ");
let l = cols.len();
for (i, slice) in cols.iter().enumerate() {
sql.push_str(slice);
if i < l - 1 {
sql.push_str(", ");
}
}
// Add additional index columns
if indices.len() > 0 {
sql.push_str(";");
sql.push_str(&indices.join(";"));
}
}
}
sql.push_str(";");
sql
})
}
/// The same as `make` but making a run-time check for sql variant
///
/// The `SqlVariant` type is populated based on the backends
/// that are being selected at compile-time.
///
/// This function panics if the provided variant is empty!
pub fn make_from(&self, variant: SqlVariant) -> String {
variant.run_for(self)
}
/// Inject a line of custom SQL into the top-level migration scope
///
/// This is a bypass to the barrel typesystem, in case there is
/// something your database supports that barrel doesn't, or if
/// there is an issue with the way that barrel represents types.
/// It does however mean that the SQL provided needs to be
/// specific for one database, meaning that future migrations
/// might become cumbersome.
pub fn inject_custom<S: Into<String>>(&mut self, sql: S) {
self.changes.push(DatabaseChange::CustomLine(sql.into()));
}
/// Automatically infer the `down` step of this migration
///
/// Will thrown an error if behaviour is ambiguous or not
/// possible to infer (e.g. revert a `drop_table`)
pub fn revert<T: SqlGenerator>(&self) -> String {
unimplemented!()
}
/// Pass a reference to a migration toolkit runner which will
/// automatically generate and execute
pub fn execute<S: SqlGenerator, T: SqlRunner>(&self, runner: &mut T) {
runner.execute(self.make::<S>());
}
/// Create a new table with a specific name
pub fn create_table<S: Into<String>, F: 'static>(&mut self, name: S, cb: F) -> &mut TableMeta
where
F: Fn(&mut Table),
{
self.changes
.push(DatabaseChange::CreateTable(Table::new(name), Rc::new(cb)));
match self.changes.last_mut().unwrap() {
&mut DatabaseChange::CreateTable(ref mut t, _) => &mut t.meta,
_ => unreachable!(),
}
}
/// Create a new table *only* if it doesn't exist yet
pub fn create_table_if_not_exists<S: Into<String>, F: 'static>(
&mut self,
name: S,
cb: F,
) -> &mut TableMeta
where
F: Fn(&mut Table),
{
self.changes.push(DatabaseChange::CreateTableIfNotExists(
Table::new(name),
Rc::new(cb),
));
match self.changes.last_mut().unwrap() {
&mut DatabaseChange::CreateTableIfNotExists(ref mut t, _) => &mut t.meta,
_ => unreachable!(),
}
}
/// Change fields on an existing table
pub fn change_table<S: Into<String>, F: 'static>(&mut self, name: S, cb: F)
where
F: Fn(&mut Table),
{
let t = Table::new(name);
let c = DatabaseChange::ChangeTable(t, Rc::new(cb));
self.changes.push(c);
}
/// Rename a table
pub fn rename_table<S: Into<String>>(&mut self, old: S, new: S) {
self.changes
.push(DatabaseChange::RenameTable(old.into(), new.into()));
}
/// Drop an existing table
pub fn drop_table<S: Into<String>>(&mut self, name: S) {
self.changes.push(DatabaseChange::DropTable(name.into()));
}
/// Only drop a table if it exists
pub fn drop_table_if_exists<S: Into<String>>(&mut self, name: S) {
self.changes
.push(DatabaseChange::DropTableIfExists(name.into()));
}
}

@ -0,0 +1,29 @@
//! Simple schema representation for migration state
trait Schemas {
/// Returns the name of a database
fn name(&self) -> String;
/// Returns a list of all tables in a database
fn tables(&self) -> Vec<String>;
/// Returns a list of all column names and types
fn columns(&self, table: &str) -> Vec<(String, Column)>;
}
trait Column {
/// Get the type of column in SQL specific terms
fn type(&self) -> String;
}
// Describe the current state of a database to apply a migration to
struct Schema {
db_name: String,
columns: Vec<String>,
}
impl Schema {
}

@ -0,0 +1,177 @@
//! A module that represents tables and columns
//!
//! A table is a collection of columns and some metadata. Creating
//! a table gives you access to the metadata fields that can only
//! be set when creating the table.
//!
//! You can also change existing tables with a closure that can
//! then access individual columns in that table.
use super::backend::SqlGenerator;
use super::{IndexChange, TableChange};
use crate::types::Type;
use std::fmt::{Debug, Formatter, Result as FmtResult};
impl Debug for TableChange {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str("TableChange")
}
}
impl Debug for IndexChange {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str("IndexChange")
}
}
#[derive(Debug, Clone)]
pub struct Table {
pub meta: TableMeta,
columns: Vec<TableChange>,
indices: Vec<IndexChange>,
}
impl Table {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
meta: TableMeta::new(name.into()),
columns: vec![],
indices: vec![],
}
}
/// Add a new column to a table
///
/// ```rust
/// # use barrel::{types, Migration};
/// # let mut m = Migration::new();
/// # m.create_table("users", |table| {
/// table.add_column("id", types::primary());
/// table.add_column("name", types::varchar(64));
/// # });
/// ```
pub fn add_column<S: Into<String>>(&mut self, name: S, _type: Type) -> &mut Type {
self.columns
.push(TableChange::AddColumn(name.into(), _type));
match self.columns.last_mut().unwrap() {
&mut TableChange::AddColumn(_, ref mut c) => c,
_ => unreachable!(),
}
}
pub fn drop_column<S: Into<String>>(&mut self, name: S) {
self.columns.push(TableChange::DropColumn(name.into()));
}
pub fn rename_column<S: Into<String>>(&mut self, old: S, new: S) {
self.columns
.push(TableChange::RenameColumn(old.into(), new.into()));
}
/// Inject a line of custom SQL into the table block
///
/// This is a bypass to the barrel typesystem, in case there is
/// something your database supports that barrel doesn't, or if
/// there is an issue with the way that barrel represents types.
/// It does however mean that the SQL provided needs to be
/// specific for one database, meaning that future migrations
/// might become cumbersome.
pub fn inject_custom<S: Into<String>>(&mut self, sql: S) {
self.columns.push(TableChange::CustomLine(sql.into()));
}
/// Add a new index to a table, spanning over multiple columns
pub fn add_index<S: Into<String>>(&mut self, name: S, columns: Type) {
match columns.inner {
crate::types::BaseType::Index(_) => {}
_ => panic!("Calling `add_index` with a non-`Index` type is not allowed!"),
}
self.indices.push(IndexChange::AddIndex {
table: self.meta.name.clone(),
index: name.into(),
columns,
});
}
/// Drop an index on this table
pub fn drop_index<S: Into<String>>(&mut self, name: S) {
self.indices.push(IndexChange::RemoveIndex(
self.meta.name.clone(),
name.into(),
));
}
/// Generate Sql for this table, returned as two vectors
///
/// The first vector (`.0`) represents all column changes done to the table,
/// the second vector (`.1`) contains all index and suffix changes.
///
/// It is very well possible for either of them to be empty,
/// although both being empty *might* signify an error.
pub fn make<T: SqlGenerator>(
&mut self,
ex: bool,
schema: Option<&str>,
) -> (Vec<String>, Vec<String>) {
use IndexChange as IC;
use TableChange as TC;
let columns = self
.columns
.iter_mut()
.map(|change| match change {
&mut TC::AddColumn(ref name, ref col) => T::add_column(ex, schema, name, &col),
&mut TC::DropColumn(ref name) => T::drop_column(name),
&mut TC::RenameColumn(ref old, ref new) => T::rename_column(old, new),
&mut TC::ChangeColumn(ref mut name, _, _) => T::alter_table(name, schema),
&mut TC::CustomLine(ref sql) => sql.clone(),
})
.collect();
let indeces = self
.indices
.iter()
.map(|change| match change {
IC::AddIndex {
index,
table,
columns,
} => T::create_index(table, schema, index, columns),
IC::RemoveIndex(_, index) => T::drop_index(index),
})
.collect();
(columns, indeces)
}
}
/// Some metadata about a table that was just created
#[derive(Debug, Clone)]
pub struct TableMeta {
pub name: String,
pub encoding: String,
}
impl TableMeta {
/// Create a new tablemeta with default values
pub fn new(name: String) -> Self {
Self {
name,
encoding: "utf-8".to_owned(),
}
}
/// Get a clone of the table name
pub fn name(&self) -> String {
self.name.clone()
}
/// Specify an encoding for this table which might vary from the main encoding
/// of your database
pub fn encoding<S: Into<String>>(&mut self, enc: S) -> &mut TableMeta {
self.encoding = enc.into();
self
}
}

@ -0,0 +1,91 @@
use crate::{
types::{self, Type},
Migration,
};
use std::fmt;
#[derive(PartialEq, Clone, Copy)]
pub enum DataTypes {
Bool,
F64,
I64,
String,
}
impl DataTypes {
#[allow(unused)]
pub fn string(&self) -> &str {
match *self {
DataTypes::Bool => "bool",
DataTypes::F64 => "f64",
DataTypes::I64 => "i64",
DataTypes::String => "String",
}
}
pub fn to_database_type(&self) -> Type {
match *self {
DataTypes::Bool => types::text(),
DataTypes::F64 => types::double(),
DataTypes::I64 => types::integer(),
DataTypes::String => types::text(),
}
}
}
impl fmt::Debug for DataTypes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let printable = match *self {
DataTypes::Bool => "",
DataTypes::F64 => "f64",
DataTypes::I64 => "i64",
DataTypes::String => "string",
};
write!(f, "{:#?}", printable)
}
}
#[derive(Clone)]
pub struct ColumnDef {
pub name: String,
pub data_type: DataTypes,
}
impl ColumnDef {
pub fn new(name: String, data_type: DataTypes) -> ColumnDef {
ColumnDef {
name: name,
data_type: data_type,
}
}
}
impl fmt::Debug for ColumnDef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {:?}", self.name, self.data_type)
}
}
pub fn create_table_if_not_exists(name: &str, columns: &Vec<ColumnDef>) {
let mut m = Migration::new();
let cols = columns.clone();
m.create_table(name, move |t| {
for cd in &cols {
let cname: &str = &cd.name;
t.add_column(cname, cd.data_type.to_database_type());
}
});
}
#[test]
fn barrel_reverse_integration() {
let cols = vec![
ColumnDef::new("name".into(), DataTypes::String),
ColumnDef::new("age".into(), DataTypes::I64),
ColumnDef::new("coolness".into(), DataTypes::F64),
ColumnDef::new("plushy_sharks_owned".into(), DataTypes::Bool),
];
// We just call this function and hope it doesn't panic
create_table_if_not_exists("users", &cols);
}

@ -0,0 +1,129 @@
/* Include some external tests */
mod cloning;
mod utils;
#[cfg(all(feature = "sqlite3", feature = "pg", feature = "mysql"))]
mod runtime;
use crate::types::{BaseType, Type, WrappedDefault};
use crate::Migration;
#[test]
fn create_multiple_tables() {
let mut migr = Migration::new();
migr.create_table("foo", |_| {});
migr.create_table("bar", |_| {});
assert!(migr.changes.len() == 2);
}
#[test]
fn create_table_if_not_exists() {
let mut migr = Migration::new();
migr.create_table_if_not_exists("foo", |_| {});
assert!(migr.changes.len() == 1);
}
#[test]
fn pin_public_api() {
// The best sql type because it's very queer 🏳🌈
let tt = Type::new(BaseType::Custom("GAY"));
assert_eq!(tt.nullable, false);
assert_eq!(tt.indexed, false);
assert_eq!(tt.unique, false);
assert_eq!(tt.increments, false);
assert_eq!(tt.default, None);
assert_eq!(tt.size, None);
assert_eq!(tt.inner, BaseType::Custom("GAY"));
}
#[test]
fn pin_struct_layout() {
// The best sql type because it's very queer 🏳🌈
let tt = Type {
nullable: false,
indexed: false,
primary: false,
unique: false,
increments: false,
default: None,
size: None,
inner: BaseType::Custom("GAY"),
};
assert_eq!(tt.nullable, false);
assert_eq!(tt.indexed, false);
assert_eq!(tt.primary, false);
assert_eq!(tt.unique, false);
assert_eq!(tt.increments, false);
assert_eq!(tt.default, None);
assert_eq!(tt.size, None);
assert_eq!(tt.inner, BaseType::Custom("GAY"));
}
#[test]
fn default_render_anytext() {
use self::WrappedDefault::*;
assert_eq!(format!("{}", AnyText("hello".into())), "hello".to_owned());
}
#[test]
fn default_render_integer() {
use self::WrappedDefault::*;
assert_eq!(format!("{}", Integer(42)), "42".to_owned());
}
#[test]
fn default_render_float() {
use self::WrappedDefault::*;
assert_eq!(format!("{}", Float(42000.0)), "42000".to_owned());
}
#[test]
fn default_render_double() {
use self::WrappedDefault::*;
assert_eq!(
format!("{}", Double(123456789.123456789)),
"123456789.12345679".to_owned()
);
}
#[test]
fn default_render_uuid() {
use self::WrappedDefault::*;
assert_eq!(
format!("{}", UUID("b616ab2a-e13c-11e8-9f32-f2801f1b9fd1".into())),
"b616ab2a-e13c-11e8-9f32-f2801f1b9fd1".to_owned()
);
}
// #[test]
// fn default_render_date() {
// use self::WrappedDefault::*;
// assert_eq!(format!("{}", Date(SystemTime::now())), "".to_owned());
// }
#[test]
fn default_render_binary() {
use self::WrappedDefault::*;
assert_eq!(
format!(
"{}",
Binary(&[
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF
])
),
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]".to_owned()
);
}
// #[test]
// fn default_render_array() {
// use self::WrappedDefault::*;
// assert_eq!(
// format!("{}", Array(vec![Type::new(BaseType::Custom("GAY"))])),
// "".to_owned()
// );
// }

@ -0,0 +1,19 @@
//! These tests check any kind of runtime-check behaviour
//!
//! They depend on all backends mostly for simplicity.
use crate::{types, Migration, SqlVariant};
/// This test mostly exists to see if we panic
#[test]
fn generate_from() {
let mut m = Migration::new();
m.create_table("testing", |table| {
table.add_column("id", types::primary());
table.add_column("name", types::varchar(64));
});
let _ = m.make_from(SqlVariant::Pg);
let _ = m.make_from(SqlVariant::Mysql);
let _ = m.make_from(SqlVariant::Sqlite);
}

@ -0,0 +1,16 @@
use crate::types;
#[test]
fn cloning_types() {
let tt = types::text();
assert_eq!(tt, tt.clone());
}
#[test]
fn equals_types() {
let t1 = types::text();
let t2 = t1.clone();
let t3 = types::integer();
assert!(t1 == t2);
assert!(t1 != t3);
}

@ -0,0 +1,13 @@
//! A unit testing module for barrel
// We can always trust these tests 👍
mod common;
#[cfg(feature = "mysql")]
mod mysql;
#[cfg(feature = "pg")]
mod pg;
#[cfg(feature = "sqlite3")]
mod sqlite3;

@ -0,0 +1,65 @@
//! All add_column combinations for mysql
#![allow(unused_imports)]
use crate::backend::{MySql, SqlGenerator};
use crate::types;
#[test]
fn text() {
let sql = MySql::add_column(true, None, "Text", &types::text());
assert_eq!(String::from("ADD COLUMN `Text` TEXT NOT NULL"), sql);
}
#[test]
fn varchar() {
let sql = MySql::add_column(true, None, "Varchar", &types::varchar(255));
assert_eq!(
String::from("ADD COLUMN `Varchar` VARCHAR(255) NOT NULL"),
sql
);
}
#[test]
fn integer() {
let sql = MySql::add_column(true, None, "Integer", &types::integer());
assert_eq!(String::from("ADD COLUMN `Integer` INTEGER NOT NULL"), sql);
}
#[test]
fn float() {
let sql = MySql::add_column(true, None, "Float", &types::float());
assert_eq!(String::from("ADD COLUMN `Float` FLOAT NOT NULL"), sql);
}
#[test]
fn double() {
let sql = MySql::add_column(true, None, "Double", &types::double());
assert_eq!(String::from("ADD COLUMN `Double` DOUBLE NOT NULL"), sql);
}
#[test]
fn boolean() {
let sql = MySql::add_column(true, None, "Boolean", &types::boolean());
assert_eq!(String::from("ADD COLUMN `Boolean` BOOLEAN NOT NULL"), sql);
}
#[test]
fn binary() {
let sql = MySql::add_column(true, None, "Binary", &types::binary());
assert_eq!(String::from("ADD COLUMN `Binary` BYTEA NOT NULL"), sql);
}
#[test]
fn date() {
let sql = MySql::add_column(true, None, "Date", &types::date());
assert_eq!(String::from("ADD COLUMN `Date` DATE NOT NULL"), sql);
}
#[test]
fn foreign() {
let sql = MySql::add_column(true, None, "Foreign", &types::foreign("posts", "id"));
assert_eq!(
String::from("ADD COLUMN `Foreign` INTEGER REFERENCES posts(id) NOT NULL"),
sql
);
}

@ -0,0 +1,37 @@
//! Some unit tests that create create tables
#![allow(unused_imports)]
use crate::backend::{MySql, SqlGenerator};
use crate::{types, Migration, Table};
#[test]
fn create_multiple_tables() {
let mut m = Migration::new();
m.create_table("artist", |t| {
t.add_column("id", types::primary());
t.add_column("name", types::text().nullable(true));
t.add_column("description", types::text().nullable(true));
t.add_column("pic", types::text().nullable(true));
t.add_column("mbid", types::text().nullable(true));
});
m.create_table("album", |t| {
t.add_column("id", types::primary());
t.add_column("name", types::text().nullable(true));
t.add_column("pic", types::text().nullable(true));
t.add_column("mbid", types::text().nullable(true));
});
assert_eq!(m.make::<MySql>(), String::from("CREATE TABLE `artist` (`id` INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` TEXT, `description` TEXT, `pic` TEXT, `mbid` TEXT);CREATE TABLE `album` (`id` INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` TEXT, `pic` TEXT, `mbid` TEXT);"));
}
#[test]
fn create_table_if_not_exists_doesnt_hit_unreachable() {
let mut m = Migration::new();
m.create_table_if_not_exists("artist", |t| {
t.add_column("id", types::primary());
t.add_column("name", types::text().nullable(true));
t.add_column("description", types::text().nullable(true));
t.add_column("pic", types::text().nullable(true));
t.add_column("mbid", types::text().nullable(true));
});
assert_eq!(m.make::<MySql>(), String::from("CREATE TABLE `artist` IF NOT EXISTS (`id` INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` TEXT, `description` TEXT, `pic` TEXT, `mbid` TEXT);"));
}

@ -0,0 +1,5 @@
//! Test mysql generation
mod add_column;
mod create_table;
mod simple;

@ -0,0 +1,50 @@
//! Other simple table/ column migrations
#![allow(unused_imports)]
use crate::backend::{MySql, SqlGenerator};
#[test]
fn create_table() {
let sql = MySql::create_table("table_to_create", None);
assert_eq!(String::from("CREATE TABLE `table_to_create`"), sql);
}
#[test]
fn create_table_with_schema() {
let sql = MySql::create_table("table_to_create", Some("my_schema"));
assert_eq!(String::from("CREATE TABLE `my_schema`.`table_to_create`"), sql);
}
#[test]
fn create_table_if_not_exists() {
let sql = MySql::create_table_if_not_exists("table_to_create", None);
assert_eq!(
String::from("CREATE TABLE `table_to_create` IF NOT EXISTS"),
sql
);
}
#[test]
fn drop_table() {
let sql = MySql::drop_table("table_to_drop", None);
assert_eq!(String::from("DROP TABLE `table_to_drop`"), sql);
}
#[test]
fn drop_table_if_exists() {
let sql = MySql::drop_table_if_exists("table_to_drop", None);
assert_eq!(String::from("DROP TABLE `table_to_drop` IF EXISTS"), sql);
}
#[test]
fn rename_table() {
let sql = MySql::rename_table("old_table", "new_table", None);
assert_eq!(String::from("RENAME TABLE `old_table` TO `new_table`"), sql);
}
#[test]
fn alter_table() {
let sql = MySql::alter_table("table_to_alter", None);
assert_eq!(String::from("ALTER TABLE `table_to_alter`"), sql);
}

@ -0,0 +1,165 @@
//! All add_column combinations for pgsql
#![allow(unused_imports)]
use crate::backend::{Pg, SqlGenerator};
use crate::types;
#[test]
fn text() {
let sql = Pg::add_column(true, None, "Text", &types::text());
assert_eq!(String::from("ADD COLUMN \"Text\" TEXT NOT NULL"), sql);
}
#[test]
fn varchar() {
let sql = Pg::add_column(true, None, "Varchar", &types::varchar(255));
assert_eq!(
String::from("ADD COLUMN \"Varchar\" VARCHAR(255) NOT NULL"),
sql
);
}
#[test]
fn integer() {
let sql = Pg::add_column(true, None, "Integer", &types::integer());
assert_eq!(String::from("ADD COLUMN \"Integer\" INTEGER NOT NULL"), sql);
}
#[test]
fn float() {
let sql = Pg::add_column(true, None, "Float", &types::float());
assert_eq!(String::from("ADD COLUMN \"Float\" FLOAT NOT NULL"), sql);
}
#[test]
fn double() {
let sql = Pg::add_column(true, None, "Double", &types::double());
assert_eq!(
String::from("ADD COLUMN \"Double\" DOUBLE PRECISION NOT NULL"),
sql
);
}
#[test]
fn boolean() {
let sql = Pg::add_column(true, None, "Boolean", &types::boolean());
assert_eq!(String::from("ADD COLUMN \"Boolean\" BOOLEAN NOT NULL"), sql);
}
#[test]
fn binary() {
let sql = Pg::add_column(true, None, "Binary", &types::binary());
assert_eq!(String::from("ADD COLUMN \"Binary\" BYTEA NOT NULL"), sql);
}
#[test]
fn date() {
let sql = Pg::add_column(true, None, "Date", &types::date());
assert_eq!(String::from("ADD COLUMN \"Date\" DATE NOT NULL"), sql);
}
#[test]
fn foreign() {
let sql = Pg::add_column(true, None, "Foreign", &types::foreign("posts", "id"));
assert_eq!(
String::from("ADD COLUMN \"Foreign\" INTEGER REFERENCES \"posts\"(id) NOT NULL"),
sql
);
}
#[test]
fn custom() {
let sql = Pg::add_column(true, None, "Point", &types::custom("POINT"));
assert_eq!(String::from("ADD COLUMN \"Point\" POINT NOT NULL"), sql);
}
#[test]
fn array_text() {
let sql = Pg::add_column(true, None, "Array of Text", &types::array(&types::text()));
assert_eq!(
String::from("ADD COLUMN \"Array of Text\" TEXT[] NOT NULL"),
sql
);
}
#[test]
fn array_varchar() {
let sql = Pg::add_column(
true,
None,
"Array of Varchar",
&types::array(&types::varchar(255)),
);
assert_eq!(
String::from("ADD COLUMN \"Array of Varchar\" VARCHAR(255)[] NOT NULL"),
sql
);
}
#[test]
fn array_integer() {
let sql = Pg::add_column(true, None, "Array of Integer", &types::array(&types::integer()));
assert_eq!(
String::from("ADD COLUMN \"Array of Integer\" INTEGER[] NOT NULL"),
sql
);
}
#[test]
fn array_float() {
let sql = Pg::add_column(true, None, "Array of Float", &types::array(&types::float()));
assert_eq!(
String::from("ADD COLUMN \"Array of Float\" FLOAT[] NOT NULL"),
sql
);
}
#[test]
fn array_double() {
let sql = Pg::add_column(true, None, "Array of Double", &types::array(&types::double()));
assert_eq!(
String::from("ADD COLUMN \"Array of Double\" DOUBLE PRECISION[] NOT NULL"),
sql
);
}
#[test]
fn array_boolean() {
let sql = Pg::add_column(true, None, "Array of Boolean", &types::array(&types::boolean()));
assert_eq!(
String::from("ADD COLUMN \"Array of Boolean\" BOOLEAN[] NOT NULL"),
sql
);
}
#[test]
fn array_binary() {
let sql = Pg::add_column(true, None, "Array of Binary", &types::array(&types::binary()));
assert_eq!(
String::from("ADD COLUMN \"Array of Binary\" BYTEA[] NOT NULL"),
sql
);
}
// #[test]
// fn array_custom() {
// let sql = Pg::add_column(true, "Array of Point", &types::array(&types::custom("POINT")));
// assert_eq!(
// String::from("ADD COLUMN \"Array of Point\" POINT[] NOT NULL"),
// sql
// );
// }
#[test]
fn array_array_integer() {
let sql = Pg::add_column(
true,
None,
"Array of Array of Integer",
&types::array(&types::array(&types::integer())),
);
assert_eq!(
String::from("ADD COLUMN \"Array of Array of Integer\" INTEGER[][] NOT NULL"),
sql
);
}

@ -0,0 +1,134 @@
//! Some unit tests that create create tables
#![allow(unused_imports)]
use crate::backend::{Pg, SqlGenerator};
use crate::{types, Migration, Table};
#[test]
fn simple_table() {
let mut m = Migration::new();
m.create_table("users", |_: &mut Table| {});
assert_eq!(m.make::<Pg>(), String::from("CREATE TABLE \"users\" ();"));
}
#[test]
fn create_table_if_not_exists_doesnt_hit_unreachable() {
let mut m = Migration::new();
m.create_table_if_not_exists("artist", |t| {
t.add_column("id", types::primary());
t.add_column("name", types::text().nullable(true));
t.add_column("description", types::text().nullable(true));
t.add_column("pic", types::text().nullable(true));
t.add_column("mbid", types::text().nullable(true));
});
assert_eq!(m.make::<Pg>(), String::from("CREATE TABLE IF NOT EXISTS \"artist\" (\"id\" SERIAL PRIMARY KEY NOT NULL, \"name\" TEXT, \"description\" TEXT, \"pic\" TEXT, \"mbid\" TEXT);"));
}
#[test]
fn basic_fields() {
let mut m = Migration::new();
m.create_table("users", |t: &mut Table| {
t.add_column("id", types::primary());
t.add_column("name", types::varchar(255));
t.add_column("age", types::integer());
t.add_column("plushy_sharks_owned", types::boolean());
});
assert_eq!(
m.make::<Pg>(),
String::from("CREATE TABLE \"users\" (\"id\" SERIAL PRIMARY KEY NOT NULL, \"name\" VARCHAR(255) NOT NULL, \"age\" INTEGER NOT NULL, \"plushy_sharks_owned\" BOOLEAN NOT NULL);")
);
}
// #[test]
// fn basic_fields_with_defaults() {
// let mut m = Migration::new();
// m.create_table("users", |t: &mut Table| {
// t.add_column("name", types::varchar(255));
// t.add_column("age", types::integer());
// t.add_column("plushy_sharks_owned", types::boolean()); // nobody is allowed plushy sharks
// });
// assert_eq!(
// m.make::<Pg>(),
// String::from("CREATE TABLE \"users\" (\"id\" SERIAL PRIMARY KEY NOT NULL, \"name\" VARCHAR(255) DEFAULT 'Anonymous' NOT NULL, \"age\" INTEGER DEFAULT '100' NOT NULL, \"plushy_sharks_owned\" BOOLEAN DEFAULT 'f' NOT NULL);")
// );
// }
#[test]
fn basic_fields_nullable() {
let mut m = Migration::new();
m.create_table("users", |t: &mut Table| {
t.add_column("id", types::primary());
t.add_column("name", types::varchar(255).nullable(true));
t.add_column("age", types::integer().nullable(true));
t.add_column("plushy_sharks_owned", types::boolean().nullable(true));
});
assert_eq!(
m.make::<Pg>(),
String::from("CREATE TABLE \"users\" (\"id\" SERIAL PRIMARY KEY NOT NULL, \"name\" VARCHAR(255), \"age\" INTEGER, \"plushy_sharks_owned\" BOOLEAN);")
);
}
// #[test]// fn simple_foreign_fields() {
// let mut m = Migration::new();
// m.create_table("users", |t: &mut Table| {
// t.add_column("id", types::primary());
// t.add_column("posts", types::foreign("poststypes::"));
// ()
// });
// assert_eq!(
// m.make::<Pg>(),
// String::from("CREATE TABLE \"users\" (\"id\" SERIAL PRIMARY KEY NOT NULL, \"posts\" INTEGER REFERENCES posts NOT NULL);")
// );
// }
#[test]
fn create_multiple_tables() {
let mut m = Migration::new();
m.create_table("artist", |t| {
t.add_column("id", types::primary());
t.add_column("name", types::text());
t.add_column("description", types::text());
t.add_column("pic", types::text());
t.add_column("mbid", types::text());
});
m.create_table("album", |t| {
t.add_column("id", types::primary());
t.add_column("name", types::text());
t.add_column("pic", types::text());
t.add_column("mbid", types::text());
});
assert_eq!(m.make::<Pg>(), String::from("CREATE TABLE \"artist\" (\"id\" SERIAL PRIMARY KEY NOT NULL, \"name\" TEXT NOT NULL, \"description\" TEXT NOT NULL, \"pic\" TEXT NOT NULL, \"mbid\" TEXT NOT NULL);CREATE TABLE \"album\" (\"id\" SERIAL PRIMARY KEY NOT NULL, \"name\" TEXT NOT NULL, \"pic\" TEXT NOT NULL, \"mbid\" TEXT NOT NULL);"));
}
#[test]
fn drop_table() {
let mut m = Migration::new();
m.drop_table("users");
assert_eq!(m.make::<Pg>(), String::from("DROP TABLE \"users\";"));
}
#[test]
fn drop_table_if_exists() {
let mut m = Migration::new();
m.drop_table_if_exists("users");
assert_eq!(
m.make::<Pg>(),
String::from("DROP TABLE IF EXISTS \"users\";")
);
}
#[test]
fn rename_table() {
let mut m = Migration::new();
m.rename_table("users", "cool_users");
assert_eq!(
m.make::<Pg>(),
String::from("ALTER TABLE \"users\" RENAME TO \"cool_users\";")
);
}

@ -0,0 +1,6 @@
//! Test pgsql generation
mod add_column;
mod create_table;
mod simple;
mod reference;

@ -0,0 +1,19 @@
#![allow(unused_imports)]
use crate::backend::{Pg, SqlGenerator};
use crate::{types, Migration, Table};
#[test]
fn in_schema() {
let sql = Pg::add_column(false, Some("schema"), "author", &types::foreign("users", "id"));
assert_eq!(sql, "\"author\" INTEGER REFERENCES \"schema\".\"users\"(id) NOT NULL");
}
#[test]
fn ext_schema() {
let sql = Pg::add_column(false, Some("schema"), "author", &types::foreign_schema("other_schema", "users", "id"));
assert_eq!(sql, "\"author\" INTEGER REFERENCES \"other_schema\".\"users\"(id) NOT NULL");
}

@ -0,0 +1,70 @@
//! Other simple table/ column migrations
#![allow(unused_imports)]
use crate::backend::{Pg, SqlGenerator};
#[test]
fn create_table() {
let sql = Pg::create_table("table_to_create", None);
assert_eq!(String::from("CREATE TABLE \"table_to_create\""), sql);
}
#[test]
fn create_table_with_schema() {
let sql = Pg::create_table("table_to_create", Some("my_schema"));
assert_eq!(
String::from("CREATE TABLE \"my_schema\".\"table_to_create\""),
sql
);
}
#[test]
fn create_table_if_not_exists() {
let sql = Pg::create_table_if_not_exists("table_to_create", None);
assert_eq!(
String::from("CREATE TABLE IF NOT EXISTS \"table_to_create\""),
sql
);
}
#[test]
fn drop_table() {
let sql = Pg::drop_table("table_to_drop", None);
assert_eq!(String::from("DROP TABLE \"table_to_drop\""), sql);
}
#[test]
fn drop_table_if_exists() {
let sql = Pg::drop_table_if_exists("table_to_drop", None);
assert_eq!(String::from("DROP TABLE IF EXISTS \"table_to_drop\""), sql);
}
#[test]
fn rename_table() {
let sql = Pg::rename_table("old_table", "new_table", None);
assert_eq!(
String::from("ALTER TABLE \"old_table\" RENAME TO \"new_table\""),
sql
);
}
#[test]
fn alter_table() {
let sql = Pg::alter_table("table_to_alter", None);
assert_eq!(String::from("ALTER TABLE \"table_to_alter\""), sql);
}
#[test]
fn drop_column() {
let sql = Pg::drop_column("column_to_drop");
assert_eq!(String::from("DROP COLUMN \"column_to_drop\""), sql);
}
#[test]
fn rename_column() {
let sql = Pg::rename_column("old_column", "new_column");
assert_eq!(
String::from("ALTER COLUMN \"old_column\" RENAME TO \"new_column\""),
sql
);
}

@ -0,0 +1,37 @@
//! Some unit tests that create create tables
#![allow(unused_imports)]
use crate::backend::{SqlGenerator, Sqlite};
use crate::{types, Migration, Table};
#[test]
fn create_multiple_tables() {
let mut m = Migration::new();
m.create_table("artist", |t| {
t.add_column("id", types::primary());
t.add_column("name", types::text().nullable(true));
t.add_column("description", types::text().nullable(true));
t.add_column("pic", types::text().nullable(true));
t.add_column("mbid", types::text().nullable(true));
});
m.create_table("album", |t| {
t.add_column("id", types::primary());
t.add_column("name", types::text().nullable(true));
t.add_column("pic", types::text().nullable(true));
t.add_column("mbid", types::text().nullable(true));
});
assert_eq!(m.make::<Sqlite>(), String::from("CREATE TABLE \"artist\" (\"id\" INTEGER NOT NULL PRIMARY KEY, \"name\" TEXT, \"description\" TEXT, \"pic\" TEXT, \"mbid\" TEXT);CREATE TABLE \"album\" (\"id\" INTEGER NOT NULL PRIMARY KEY, \"name\" TEXT, \"pic\" TEXT, \"mbid\" TEXT);"));
}
#[test]
fn create_table_if_not_exists_doesnt_hit_unreachable() {
let mut m = Migration::new();
m.create_table_if_not_exists("artist", |t| {
t.add_column("id", types::primary());
t.add_column("name", types::text().nullable(true));
t.add_column("description", types::text().nullable(true));
t.add_column("pic", types::text().nullable(true));
t.add_column("mbid", types::text().nullable(true));
});
assert_eq!(m.make::<Sqlite>(), String::from("CREATE TABLE IF NOT EXISTS \"artist\" (\"id\" INTEGER NOT NULL PRIMARY KEY, \"name\" TEXT, \"description\" TEXT, \"pic\" TEXT, \"mbid\" TEXT);"));
}

@ -0,0 +1,4 @@
//! A few simple tests for the sqlite3 string backend
mod create_table;
mod simple;

@ -0,0 +1,56 @@
//! Other simple table/ column migrations
#![allow(unused_imports)]
use crate::backend::{SqlGenerator, Sqlite};
#[test]
fn create_table() {
let sql = Sqlite::create_table("table_to_create", None);
assert_eq!(String::from("CREATE TABLE \"table_to_create\""), sql);
}
#[test]
fn create_table_with_schema() {
let sql = Sqlite::create_table("table_to_create", Some("my_schema"));
assert_eq!(
String::from("CREATE TABLE \"my_schema\".\"table_to_create\""),
sql
);
}
#[test]
fn create_table_if_not_exists() {
let sql = Sqlite::create_table_if_not_exists("table_to_create", None);
assert_eq!(
String::from("CREATE TABLE IF NOT EXISTS \"table_to_create\""),
sql
);
}
#[test]
fn drop_table() {
let sql = Sqlite::drop_table("table_to_drop", None);
assert_eq!(String::from("DROP TABLE \"table_to_drop\""), sql);
}
#[test]
fn drop_table_if_exists() {
let sql = Sqlite::drop_table_if_exists("table_to_drop", None);
assert_eq!(String::from("DROP TABLE IF EXISTS \"table_to_drop\""), sql);
}
#[test]
fn rename_table() {
let sql = Sqlite::rename_table("old_table", "new_table", None);
assert_eq!(
String::from("ALTER TABLE \"old_table\" RENAME TO \"new_table\""),
sql
);
}
#[test]
fn alter_table() {
let sql = Sqlite::alter_table("table_to_alter", None);
assert_eq!(String::from("ALTER TABLE \"table_to_alter\""), sql);
}

@ -0,0 +1,116 @@
//! Builder API's module
use super::impls::{BaseType, WrapVec};
use crate::types::Type;
/// A standard primary numeric key type
///
/// It's 64-bit wide, can't be null or non-unique
/// and auto-increments on inserts.
/// Maps to `primary` on `Pg` and manually enforces
/// this behaviour for other Sql variants.
pub fn primary() -> Type {
Type::new(BaseType::Primary)
.nullable(true) // Primary keys are non-null implicitly
.increments(true) // This is ignored for now
.primary(false) // Primary keys are primary implictly
.unique(false) // Primary keys are unique implicitly
.indexed(false)
}
/// A (standardised) UUID primary key type
///
/// Similar to `primary()`, but uses a standard
/// layout UUID type, mapping to `uuid` on `Pg`
/// and not supported by all Sql variants.
pub fn uuid() -> Type {
Type::new(BaseType::UUID)
.nullable(false)
.unique(true)
.indexed(true)
}
/// Create a basic integer type
pub fn integer() -> Type {
Type::new(BaseType::Integer)
}
/// A 32-bit floating point type
pub fn float() -> Type {
Type::new(BaseType::Float)
}
/// A 64-bit floating point type
pub fn double() -> Type {
Type::new(BaseType::Double)
}
/// A boolean data type (true, false)
pub fn boolean() -> Type {
Type::new(BaseType::Boolean)
}
/// A fixed-length string type
pub fn varchar(len: usize) -> Type {
Type::new(BaseType::Varchar(len))
}
/// A variable-length string type
pub fn text() -> Type {
Type::new(BaseType::Text)
}
/// A json-type column – not supported by all backends
pub fn json() -> Type {
Type::new(BaseType::Json)
}
/// Embed binary data
pub fn binary<'inner>() -> Type {
Type::new(BaseType::Binary)
}
/// Create a column that points to some foreign table
pub fn foreign<S, I>(table: S, keys: I) -> Type
where
S: Into<String>,
I: Into<WrapVec<String>>,
{
Type::new(BaseType::Foreign(None, table.into(), keys.into()))
}
/// Like `foreign(...)` but letting you provide an external schema
///
/// This function is important when making cross-schema references
pub fn foreign_schema<S, I>(schema: S, table: S, keys: I) -> Type
where
S: Into<String>,
I: Into<WrapVec<String>>,
{
Type::new(BaseType::Foreign(
Some(schema.into()),
table.into(),
keys.into(),
))
}
/// Any custom SQL type that is embedded into a migration
pub fn custom(sql: &'static str) -> Type {
Type::new(BaseType::Custom(sql))
}
/// An SQL date type
pub fn date() -> Type {
Type::new(BaseType::Date)
}
/// Create an array of inner types
pub fn array(inner: &Type) -> Type {
Type::new(BaseType::Array(Box::new(inner.get_inner())))
}
/// Create an index over multiple, existing columns of the same type
pub fn index<S: Into<String>>(columns: Vec<S>) -> Type {
let vec: Vec<String> = columns.into_iter().map(|s| s.into()).collect();
Type::new(BaseType::Index(vec))
}

@ -0,0 +1,89 @@
use std::fmt::{self, Display, Formatter};
use std::time::SystemTime;
use super::Type;
#[derive(PartialEq, Debug, Clone)]
pub enum WrappedDefault<'outer> {
/// Any text information
AnyText(&'outer str),
/// Simple integer
Integer(i64),
/// Floating point number
Float(f32),
/// Like Float but `~ ~ d o u b l e p r e c i s i o n ~ ~`
Double(f64),
/// A unique identifier type
UUID(String), // TODO: Change to UUID type
/// True or False
Boolean(bool),
/// Date And Time
Date(SystemTime),
/// <inconceivable jibberish>
Binary(&'outer [u8]),
/// Foreign key to other table
Foreign(Box<Type>),
// I have no idea what you are – but I *like* it
Custom(&'static str),
/// Any of the above, but **many** of them
Array(Vec<Type>),
}
impl<'outer> Display for WrappedDefault<'outer> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use self::WrappedDefault::*;
write!(
f,
"{}",
&match *self {
AnyText(ref val) => format!("{}", val),
Integer(ref val) => format!("{}", val),
Float(ref val) => format!("{}", val),
Double(ref val) => format!("{}", val),
UUID(ref val) => format!("{}", val),
Boolean(ref val) => format!("{}", val),
Date(ref val) => format!("{:?}", val),
Binary(ref val) => format!("{:?}", val),
Foreign(ref val) => format!("{:?}", val),
Custom(ref val) => format!("{}", val),
Array(ref val) => format!("{:?}", val),
}
)
}
}
impl From<&'static str> for WrappedDefault<'static> {
fn from(s: &'static str) -> Self {
WrappedDefault::AnyText(s)
}
}
impl From<i64> for WrappedDefault<'static> {
fn from(s: i64) -> Self {
WrappedDefault::Integer(s)
}
}
impl From<f32> for WrappedDefault<'static> {
fn from(s: f32) -> Self {
WrappedDefault::Float(s)
}
}
impl From<f64> for WrappedDefault<'static> {
fn from(s: f64) -> Self {
WrappedDefault::Double(s)
}
}
impl From<bool> for WrappedDefault<'static> {
fn from(s: bool) -> Self {
WrappedDefault::Boolean(s)
}
}
impl From<SystemTime> for WrappedDefault<'static> {
fn from(s: SystemTime) -> Self {
WrappedDefault::Date(s)
}
}

@ -0,0 +1,165 @@
//! Implementation specifics for the type system
use super::WrappedDefault;
/// A smol wrapper around `Vec<T>` to get around the orphan rules
#[derive(PartialEq, Debug, Clone)]
pub struct WrapVec<T>(pub Vec<T>);
/// Core type enum, describing the basic type
#[derive(PartialEq, Debug, Clone)]
pub enum BaseType {
/// Strings
Text,
/// Like a String but worse
Varchar(usize),
/// Primary key (utility for incrementing integer – postgres supports this, we just mirror it)
Primary,
/// Simple integer
Integer,
/// Floating point number
Float,
/// Like Float but `~ ~ d o u b l e p r e c i s i o n ~ ~`
Double,
/// A unique identifier type
UUID,
/// True or False
Boolean,
/// Json encoded data
Json,
/// Date And Time
Date,
/// <inconceivable jibberish>
Binary,
/// Foreign key to other table
Foreign(Option<String>, String, WrapVec<String>),
/// I have no idea what you are – but I *like* it
Custom(&'static str),
/// Any of the above, but **many** of them
Array(Box<BaseType>),
/// Indexing over multiple columns
Index(Vec<String>),
}
/// A database column type and all the metadata attached to it
///
/// Using this struct directly is not recommended. Instead, you should be
/// using the constructor APIs in the `types` module.
///
/// A `Type` is an enum provided to other `barrel` APIs in order
/// to generate SQL datatypes. Working with them directly is possible
/// but not recommended.
///
/// Instead, you can use these helper functions to construct `Type` enums of
/// different...types and constraints. Field metadata is added via chainable
/// factory pattern functions.
///
/// ## Default values
///
/// If no additional arguments are provided, some assumptions will be made
/// about the metadata of a column type.
///
/// - `nullable`: `false`
/// - `indexed`: `false`
/// - `unique`: `false`
/// - `default`: `None`
/// - `size`: `None` (which will error if size is important)
///
/// ## Examples
///
/// ```rust,norun
/// extern crate barrel;
/// use barrel::types::*;
///
/// // Make your own Primary key :)
/// let col = integer().increments(true).unique(true);
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct Type {
pub nullable: bool,
pub unique: bool,
pub increments: bool,
pub indexed: bool,
pub primary: bool,
pub default: Option<WrappedDefault<'static>>,
pub size: Option<usize>,
pub inner: BaseType,
}
/// This is a public API, be considered about breaking thigns
#[cfg_attr(rustfmt, rustfmt_skip)]
impl Type {
pub(crate) fn new(inner: BaseType) -> Self {
Self {
nullable: false,
unique: false,
increments: false,
indexed: false,
primary: false,
default: None,
size: None,
inner,
}
}
/// Function used to hide the inner type to outside users (sneaky, I know)
pub(crate) fn get_inner(&self) -> BaseType {
self.inner.clone()
}
/// Set the nullability of this type
pub fn nullable(self, arg: bool) -> Self {
Self { nullable: arg, ..self }
}
/// Set the uniqueness of this type
pub fn unique(self, arg: bool) -> Self {
Self { unique: arg, ..self }
}
/// Specify if this type should auto-increment
pub fn increments(self, arg: bool) -> Self {
Self { increments: arg, ..self }
}
/// Specify if this type should be indexed by your SQL implementation
pub fn indexed(self, arg: bool) -> Self {
Self { indexed: arg, ..self }
}
/// Specify if this type should be a primary key
pub fn primary(self, arg: bool) -> Self {
Self { primary: arg, ..self }
}
/// Provide a default value for a type column
pub fn default(self, arg: impl Into<WrappedDefault<'static>>) -> Self {
Self { default: Some(arg.into()), ..self }
}
/// Specify a size limit (important or varchar & similar)
pub fn size(self, arg: usize) -> Self {
Self { size: Some(arg), ..self }
}
}
impl<'a> From<&'a str> for WrapVec<String> {
fn from(s: &'a str) -> Self {
WrapVec(vec![s.into()])
}
}
impl From<String> for WrapVec<String> {
fn from(s: String) -> Self {
WrapVec(vec![s])
}
}
impl<I> From<Vec<I>> for WrapVec<String>
where
I: Into<String>,
{
fn from(v: Vec<I>) -> Self {
WrapVec(v.into_iter().map(|s| s.into()).collect())
}
}

@ -0,0 +1,9 @@
//! Types constructor module
mod builders;
mod defaults;
mod impls;
pub use self::builders::*;
pub use self::defaults::WrappedDefault;
pub use self::impls::{BaseType, Type, WrapVec};
Loading…
Cancel
Save