# 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
## Table of Contents
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)
## Types
- **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)**
## Tables
- 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)**
## Strings
- 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)**
## Functions
- 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 precedence 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)**
## Properties
- 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)**
## Variables
- 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)**
## Conditional Expressions & Equality
- 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)**
## Blocks
- 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)**
## Whitespace
- 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)**
## Commas
- 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)**
## Semicolons
- **Nope.** Separate statements onto multiple lines.
```lua
-- bad
local whatever = 'sure';
a = 1; b = 2
-- good
local whatever = 'sure'
a = 1
b = 2
```
**[[⬆]](#TOC)**
## Type Casting & Coercion
- 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)**
## Naming Conventions
- 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
```
## Modules
- 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)**
## Testing
- 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)**
## License
- 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)**