|
|
@@ -0,0 +1,733 @@ |
|
|
|
# Lua Style Guide |
|
|
|
|
|
|
|
This style guide contains a list of guidelines that we try to follow for Rspamd. |
|
|
|
|
|
|
|
This guide is forked from https://github.com/Olivine-Labs/lua-style-guide |
|
|
|
|
|
|
|
|
|
|
|
## <a name='TOC'>Table of Contents</a> |
|
|
|
|
|
|
|
1. [Types](#types) |
|
|
|
1. [Tables](#tables) |
|
|
|
1. [Strings](#strings) |
|
|
|
1. [Functions](#functions) |
|
|
|
1. [Properties](#properties) |
|
|
|
1. [Variables](#variables) |
|
|
|
1. [Conditional Expressions & Equality](#conditionals) |
|
|
|
1. [Blocks](#blocks) |
|
|
|
1. [Whitespace](#whitespace) |
|
|
|
1. [Commas](#commas) |
|
|
|
1. [Semicolons](#semicolons) |
|
|
|
1. [Type Casting & Coercion](#type-coercion) |
|
|
|
1. [Naming Conventions](#naming-conventions) |
|
|
|
1. [Accessors](#accessors) |
|
|
|
1. [Constructors](#constructors) |
|
|
|
1. [Modules](#modules) |
|
|
|
1. [Testing](#testing) |
|
|
|
1. [License](#license) |
|
|
|
|
|
|
|
## <a name='types'>Types</a> |
|
|
|
|
|
|
|
- **Primitives**: When you access a primitive type you work directly on its value |
|
|
|
|
|
|
|
+ `string` |
|
|
|
+ `number` |
|
|
|
+ `boolean` |
|
|
|
+ `nil` |
|
|
|
|
|
|
|
```lua |
|
|
|
local foo = 1 |
|
|
|
local bar = foo |
|
|
|
|
|
|
|
bar = 9 |
|
|
|
|
|
|
|
print(foo, bar) -- => 1 9 |
|
|
|
``` |
|
|
|
|
|
|
|
- **Complex**: When you access a complex type you work on a reference to its value |
|
|
|
|
|
|
|
+ `table` |
|
|
|
+ `function` |
|
|
|
+ `userdata` |
|
|
|
|
|
|
|
```lua |
|
|
|
local foo = { 1, 2 } |
|
|
|
local bar = foo |
|
|
|
|
|
|
|
bar[0] = 9 |
|
|
|
foo[1] = 3 |
|
|
|
|
|
|
|
print(foo[0], bar[0]) -- => 9 9 |
|
|
|
print(foo[1], bar[1]) -- => 3 3 |
|
|
|
print(foo[2], bar[2]) -- => 2 2 |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
## <a name='tables'>Tables</a> |
|
|
|
|
|
|
|
- Use the constructor syntax for table property creation where possible. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local player = {} |
|
|
|
player.name = 'Jack' |
|
|
|
player.class = 'Rogue' |
|
|
|
|
|
|
|
-- good |
|
|
|
local player = { |
|
|
|
name = 'Jack', |
|
|
|
class = 'Rogue' |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
- Define functions externally to table definition. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local player = { |
|
|
|
attack = function() |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
} |
|
|
|
|
|
|
|
-- good |
|
|
|
local function attack() |
|
|
|
end |
|
|
|
|
|
|
|
local player = { |
|
|
|
attack = attack |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
## <a name='strings'>Strings</a> |
|
|
|
|
|
|
|
- Use single quotes `''` for strings. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local name = "Bob Parr" |
|
|
|
|
|
|
|
-- good |
|
|
|
local name = 'Bob Parr' |
|
|
|
|
|
|
|
-- bad |
|
|
|
local fullName = "Bob " .. self.lastName |
|
|
|
|
|
|
|
-- good |
|
|
|
local fullName = 'Bob ' .. self.lastName |
|
|
|
``` |
|
|
|
|
|
|
|
- Strings longer than 80 characters should be written across multiple lines |
|
|
|
using concatenation. This allows you to indent nicely. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.' |
|
|
|
|
|
|
|
-- bad |
|
|
|
local errorMessage = 'This is a super long error that \ |
|
|
|
was thrown because of Batman. \ |
|
|
|
When you stop to think about \ |
|
|
|
how Batman had anything to do \ |
|
|
|
with this, you would get nowhere \ |
|
|
|
fast.' |
|
|
|
|
|
|
|
|
|
|
|
-- bad |
|
|
|
local errorMessage = [[This is a super long error that |
|
|
|
was thrown because of Batman. |
|
|
|
When you stop to think about |
|
|
|
how Batman had anything to do |
|
|
|
with this, you would get nowhere |
|
|
|
fast.]] |
|
|
|
|
|
|
|
-- good |
|
|
|
local errorMessage = 'This is a super long error that ' .. |
|
|
|
'was thrown because of Batman. ' .. |
|
|
|
'When you stop to think about ' .. |
|
|
|
'how Batman had anything to do ' .. |
|
|
|
'with this, you would get nowhere ' .. |
|
|
|
'fast.' |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
|
|
|
|
## <a name='functions'>Functions</a> |
|
|
|
- Prefer lots of small functions to large, complex functions. [Smalls Functions Are Good For The Universe](http://kikito.github.io/blog/2012/03/16/small-functions-are-good-for-the-universe/). |
|
|
|
|
|
|
|
- Prefer function syntax over variable syntax. This helps differentiate |
|
|
|
between named and anonymous functions. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local nope = function(name, options) |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
|
|
|
|
-- good |
|
|
|
local function yup(name, options) |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Never name a parameter `arg`, this will take precendence over the `arg` object that is given to every function scope in older versions of Lua. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local function nope(name, options, arg) |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
|
|
|
|
-- good |
|
|
|
local function yup(name, options, ...) |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Perform validation early and return as early as possible. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local is_good_name = function(name, options, arg) |
|
|
|
local is_good = #name > 3 |
|
|
|
is_good = is_good and #name < 30 |
|
|
|
|
|
|
|
-- ...stuff... |
|
|
|
|
|
|
|
return is_bad |
|
|
|
end |
|
|
|
|
|
|
|
-- good |
|
|
|
local is_good_name = function(name, options, args) |
|
|
|
if #name < 3 or #name > 30 then return false end |
|
|
|
|
|
|
|
-- ...stuff... |
|
|
|
|
|
|
|
return true |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
|
|
|
|
## <a name='properties'>Properties</a> |
|
|
|
|
|
|
|
- Use dot notation when accessing known properties. |
|
|
|
|
|
|
|
```lua |
|
|
|
local luke = { |
|
|
|
jedi = true, |
|
|
|
age = 28 |
|
|
|
} |
|
|
|
|
|
|
|
-- bad |
|
|
|
local isJedi = luke['jedi'] |
|
|
|
|
|
|
|
-- good |
|
|
|
local isJedi = luke.jedi |
|
|
|
``` |
|
|
|
|
|
|
|
- Use subscript notation `[]` when accessing properties with a variable |
|
|
|
or if using a table as a list. |
|
|
|
|
|
|
|
```lua |
|
|
|
local luke = { |
|
|
|
jedi = true, |
|
|
|
age = 28 |
|
|
|
} |
|
|
|
|
|
|
|
local function getProp(prop) |
|
|
|
return luke[prop] |
|
|
|
end |
|
|
|
|
|
|
|
local isJedi = getProp('jedi') |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
|
|
|
|
## <a name='variables'>Variables</a> |
|
|
|
|
|
|
|
- Always use `local` to declare variables. Not doing so will result in |
|
|
|
global variables to avoid polluting the global namespace. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
superPower = SuperPower() |
|
|
|
|
|
|
|
-- good |
|
|
|
local superPower = SuperPower() |
|
|
|
``` |
|
|
|
|
|
|
|
- Assign variables at the top of their scope where possible. This makes it |
|
|
|
easier to check for existing variables. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local bad = function() |
|
|
|
test() |
|
|
|
print('doing stuff..') |
|
|
|
|
|
|
|
//..other stuff.. |
|
|
|
|
|
|
|
local name = getName() |
|
|
|
|
|
|
|
if name == 'test' then |
|
|
|
return false |
|
|
|
end |
|
|
|
|
|
|
|
return name |
|
|
|
end |
|
|
|
|
|
|
|
-- good |
|
|
|
local function good() |
|
|
|
local name = getName() |
|
|
|
|
|
|
|
test() |
|
|
|
print('doing stuff..') |
|
|
|
|
|
|
|
//..other stuff.. |
|
|
|
|
|
|
|
if name == 'test' then |
|
|
|
return false |
|
|
|
end |
|
|
|
|
|
|
|
return name |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
|
|
|
|
## <a name='conditionals'>Conditional Expressions & Equality</a> |
|
|
|
|
|
|
|
- False and nil are *falsy* in conditional expressions. All else is true. |
|
|
|
|
|
|
|
```lua |
|
|
|
local str = '' |
|
|
|
|
|
|
|
if str then |
|
|
|
-- true |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Use shortcuts when you can, unless you need to know the difference between |
|
|
|
false and nil. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
if name ~= nil then |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
|
|
|
|
-- good |
|
|
|
if name then |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Prefer *true* statements over *false* statements where it makes sense. |
|
|
|
Prioritize truthy conditions when writing multiple conditions. |
|
|
|
|
|
|
|
```lua |
|
|
|
--bad |
|
|
|
if not thing then |
|
|
|
-- ...stuff... |
|
|
|
else |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
|
|
|
|
--good |
|
|
|
if thing then |
|
|
|
-- ...stuff... |
|
|
|
else |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Prefer defaults to `else` statements where it makes sense. This results in |
|
|
|
less complex and safer code at the expense of variable reassignment, so |
|
|
|
situations may differ. |
|
|
|
|
|
|
|
```lua |
|
|
|
--bad |
|
|
|
local function full_name(first, last) |
|
|
|
local name |
|
|
|
|
|
|
|
if first and last then |
|
|
|
name = first .. ' ' .. last |
|
|
|
else |
|
|
|
name = 'John Smith' |
|
|
|
end |
|
|
|
|
|
|
|
return name |
|
|
|
end |
|
|
|
|
|
|
|
--good |
|
|
|
local function full_name(first, last) |
|
|
|
local name = 'John Smith' |
|
|
|
|
|
|
|
if first and last then |
|
|
|
name = first .. ' ' .. last |
|
|
|
end |
|
|
|
|
|
|
|
return name |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Short ternaries are okay. |
|
|
|
|
|
|
|
```lua |
|
|
|
local function default_name(name) |
|
|
|
-- return the default 'Waldo' if name is nil |
|
|
|
return name or 'Waldo' |
|
|
|
end |
|
|
|
|
|
|
|
local function brew_coffee(machine) |
|
|
|
return machine and machine.is_loaded and 'coffee brewing' or 'fill your water' |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
|
|
|
|
## <a name='blocks'>Blocks</a> |
|
|
|
|
|
|
|
- Single line blocks are okay for *small* statements. Try to keep lines to 80 characters. |
|
|
|
Indent lines if they overflow past the limit. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- good |
|
|
|
if test then return false end |
|
|
|
|
|
|
|
-- good |
|
|
|
if test then |
|
|
|
return false |
|
|
|
end |
|
|
|
|
|
|
|
-- bad |
|
|
|
if test < 1 and do_complicated_function(test) == false or seven == 8 and nine == 10 then do_other_complicated_function()end |
|
|
|
|
|
|
|
-- good |
|
|
|
if test < 1 and do_complicated_function(test) == false or |
|
|
|
seven == 8 and nine == 10 then |
|
|
|
|
|
|
|
do_other_complicated_function() |
|
|
|
return false |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
|
|
|
|
## <a name='whitespace'>Whitespace</a> |
|
|
|
|
|
|
|
- Use soft tabs set to 2 spaces. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
function() |
|
|
|
∙∙∙∙local name |
|
|
|
end |
|
|
|
|
|
|
|
-- bad |
|
|
|
function() |
|
|
|
∙local name |
|
|
|
end |
|
|
|
|
|
|
|
-- good |
|
|
|
function() |
|
|
|
∙∙local name |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Place 1 space before opening and closing braces. Place no spaces around parens. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local test = {one=1} |
|
|
|
|
|
|
|
-- good |
|
|
|
local test = { one = 1 } |
|
|
|
|
|
|
|
-- bad |
|
|
|
dog.set('attr',{ |
|
|
|
age = '1 year', |
|
|
|
breed = 'Bernese Mountain Dog' |
|
|
|
}) |
|
|
|
|
|
|
|
-- good |
|
|
|
dog.set('attr', { |
|
|
|
age = '1 year', |
|
|
|
breed = 'Bernese Mountain Dog' |
|
|
|
}) |
|
|
|
``` |
|
|
|
|
|
|
|
- Place an empty newline at the end of the file. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
(function(global) |
|
|
|
-- ...stuff... |
|
|
|
end)(self) |
|
|
|
``` |
|
|
|
|
|
|
|
```lua |
|
|
|
-- good |
|
|
|
(function(global) |
|
|
|
-- ...stuff... |
|
|
|
end)(self) |
|
|
|
|
|
|
|
``` |
|
|
|
|
|
|
|
- Surround operators with spaces. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local thing=1 |
|
|
|
thing = thing-1 |
|
|
|
thing = thing*1 |
|
|
|
thing = 'string'..'s' |
|
|
|
|
|
|
|
-- good |
|
|
|
local thing = 1 |
|
|
|
thing = thing - 1 |
|
|
|
thing = thing * 1 |
|
|
|
thing = 'string' .. 's' |
|
|
|
``` |
|
|
|
|
|
|
|
- Use one space after commas. |
|
|
|
|
|
|
|
```lua |
|
|
|
--bad |
|
|
|
local thing = {1,2,3} |
|
|
|
thing = {1 , 2 , 3} |
|
|
|
thing = {1 ,2 ,3} |
|
|
|
|
|
|
|
--good |
|
|
|
local thing = {1, 2, 3} |
|
|
|
``` |
|
|
|
|
|
|
|
- Add a line break after multiline blocks. |
|
|
|
|
|
|
|
```lua |
|
|
|
--bad |
|
|
|
if thing then |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
function derp() |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
local wat = 7 |
|
|
|
|
|
|
|
--good |
|
|
|
if thing then |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
|
|
|
|
function derp() |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
|
|
|
|
local wat = 7 |
|
|
|
``` |
|
|
|
|
|
|
|
- Delete unnecessary whitespace at the end of lines. |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
## <a name='commas'>Commas</a> |
|
|
|
|
|
|
|
- Leading commas aren't okay. An ending comma on the last item is okay but discouraged. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local thing = { |
|
|
|
once = 1 |
|
|
|
, upon = 2 |
|
|
|
, aTime = 3 |
|
|
|
} |
|
|
|
|
|
|
|
-- good |
|
|
|
local thing = { |
|
|
|
once = 1, |
|
|
|
upon = 2, |
|
|
|
aTime = 3 |
|
|
|
} |
|
|
|
|
|
|
|
-- okay |
|
|
|
local thing = { |
|
|
|
once = 1, |
|
|
|
upon = 2, |
|
|
|
aTime = 3, |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
|
|
|
|
## <a name='semicolons'>Semicolons</a> |
|
|
|
|
|
|
|
- **Nope.** Separate statements onto multiple lines. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local whatever = 'sure'; |
|
|
|
a = 1; b = 2 |
|
|
|
|
|
|
|
-- good |
|
|
|
local whatever = 'sure' |
|
|
|
a = 1 |
|
|
|
b = 2 |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
|
|
|
|
## <a name='type-coercion'>Type Casting & Coercion</a> |
|
|
|
|
|
|
|
- Perform type coercion at the beginning of the statement. Use the built-in functions. (`tostring`, `tonumber`, etc.) |
|
|
|
|
|
|
|
- Use `tostring` for strings if you need to cast without string concatenation. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local totalScore = reviewScore .. '' |
|
|
|
|
|
|
|
-- good |
|
|
|
local totalScore = tostring(reviewScore) |
|
|
|
``` |
|
|
|
|
|
|
|
- Use `tonumber` for Numbers. |
|
|
|
|
|
|
|
```lua |
|
|
|
local inputValue = '4' |
|
|
|
|
|
|
|
-- bad |
|
|
|
local val = inputValue * 1 |
|
|
|
|
|
|
|
-- good |
|
|
|
local val = tonumber(inputValue) |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
|
|
|
|
## <a name='naming-conventions'>Naming Conventions</a> |
|
|
|
|
|
|
|
- Avoid single letter names. Be descriptive with your naming. You can get |
|
|
|
away with single-letter names when they are variables in loops. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local function q() |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
|
|
|
|
-- good |
|
|
|
local function query() |
|
|
|
-- ..stuff.. |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Use underscores for ignored variables in loops. |
|
|
|
|
|
|
|
```lua |
|
|
|
--good |
|
|
|
for _, name in pairs(names) do |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Use snake_case when naming objects, functions, and instances. Tend towards |
|
|
|
verbosity if unsure about naming. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local OBJEcttsssss = {} |
|
|
|
local thisIsMyObject = {} |
|
|
|
|
|
|
|
local c = function() |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
|
|
|
|
-- good |
|
|
|
local this_is_my_object = {} |
|
|
|
|
|
|
|
local function do_that_thing() |
|
|
|
-- ...stuff... |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
- Use PascalCase for factories. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- bad |
|
|
|
local player = require('player') |
|
|
|
|
|
|
|
-- good |
|
|
|
local Player = require('player') |
|
|
|
local me = Player({ name = 'Jack' }) |
|
|
|
``` |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
- Use `is` or `has` for boolean-returning functions that are part of tables. |
|
|
|
|
|
|
|
```lua |
|
|
|
--bad |
|
|
|
local function evil(alignment) |
|
|
|
return alignment < 100 |
|
|
|
end |
|
|
|
|
|
|
|
--good |
|
|
|
local function is_evil(alignment) |
|
|
|
return alignment < 100 |
|
|
|
end |
|
|
|
``` |
|
|
|
|
|
|
|
## <a name='modules'>Modules</a> |
|
|
|
|
|
|
|
- The module should return a table or function. |
|
|
|
- The module should not use the global namespace for anything ever. The |
|
|
|
module should be a closure. |
|
|
|
- The file should be named like the module. |
|
|
|
|
|
|
|
```lua |
|
|
|
-- thing.lua |
|
|
|
local thing = { } |
|
|
|
|
|
|
|
local meta = { |
|
|
|
__call = function(self, key, vars) |
|
|
|
print key |
|
|
|
end |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return setmetatable(thing, meta) |
|
|
|
``` |
|
|
|
|
|
|
|
- Note that modules are [loaded as singletons](http://lua-users.org/wiki/TheEssenceOfLoadingCode) |
|
|
|
and therefore should usually be factories (a function returning a new instance of a table) |
|
|
|
unless static (like utility libraries.) |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
## <a name='testing'>Testing</a> |
|
|
|
|
|
|
|
- Use [telescope](https://github.com/norman/telescope) for unit tests and Robot framework for functional testing. |
|
|
|
Unit tests can rely on LuaJIT ffi module if C function testing is required. |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |
|
|
|
|
|
|
|
## <a name='license'>License</a> |
|
|
|
|
|
|
|
- Released under CC0 (Public Domain). |
|
|
|
Information can be found at [http://creativecommons.org/publicdomain/zero/1.0/](http://creativecommons.org/publicdomain/zero/1.0/). |
|
|
|
|
|
|
|
**[[⬆]](#TOC)** |