Module:Cargo/config
Jump to navigation
Jump to search
This is the configuration file for Module:Cargo. This file can be edited to allow easy translation/porting of the module to other wikis.
The above documentation is transcluded from Module:Cargo/config/doc.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
-------------------------------------------------------------------------------
--
-- Module:Cargo
--
-- Common tasks for the cargo extension are generalized into handy functions
-- in this meta module
-------------------------------------------------------------------------------
local getArgs = require('Module:Arguments').getArgs
local m_util = require('Module:Util')
local cargo = mw.ext.cargo
-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = mw.loadData('Module:Cargo/config')
local i18n = cfg.i18n
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local m_cargo = {}
--
-- Cargo function wrappers
--
function m_cargo.declare(frame, args)
return frame:callParserFunction('#cargo_declare:', args)
end
function m_cargo.attach(frame, args)
return frame:callParserFunction('#cargo_attach:', args)
end
function m_cargo.store(frame, values, args)
-- Calls the cargo_store parser function and ensures the values passed are casted properly
--
-- Value handling:
-- tables - automatically concat
-- booleans - automatically casted to 1 or 0 to ensure they're stored properly
--
-- Arguments:
-- frame - frame object
-- values - table of field/value pairs to store
-- args - any additional arguments
-- sep - separator to use for concat
-- store_empty - if specified, allow storing empty rows
-- debug - send the converted values to the lua debug log
args = args or {}
args.sep = args.sep or {}
local i = 0
for k, v in pairs(values) do
i = i + 1
if type(v) == 'table' then
if #v == 0 then
i = i - 1
values[k] = nil
else
values[k] = table.concat(v, args.sep[k] or ',')
end
elseif type(v) == 'boolean' then
if v == true then
v = '1'
elseif v == false then
v = '0'
end
values[k] = v
end
end
-- i must be greater then 1 since we at least expect the _table argument to be set, even if no values are set
if i > 1 or args.store_empty then
if args.debug ~= nil then
mw.logObject(values)
end
return frame:callParserFunction('#cargo_store:', values)
end
end
function m_cargo.query(tables, fields, query, args)
-- Wrapper for mw.ext.cargo.query that helps to work around some bugs
--
-- Current workarounds:
-- field names will be "aliased" to themselves
--
-- Takes 3 arguments:
-- tables - array containing tables
-- fields - array containing fields; these will automatically be renamed to the way they are specified to work around bugs when results are returned
-- query - array containing cargo sql clauses
-- args
-- args.keep_empty
-- Cargo bug workaround
args = args or {}
for i, field in ipairs(fields) do
-- already has some alternate name set, so do not do this.
if string.find(field, '=') == nil then
fields[i] = string.format('%s=%s', field, field)
end
end
query.limit = query.limit or cfg.limit*100
query.offset = query.offset or 0
local results = {}
repeat
local result = cargo.query(table.concat(tables, ','), table.concat(fields, ','), query)
query.offset = query.offset + #result
for _,v in ipairs(result) do
results[#results + 1] = v
end
until (#result < cfg.limit) or (#results >= query.limit)
if args.keep_empty == nil then
for _, row in ipairs(results) do
for k, v in pairs(row) do
if v == "" then
row[k] = nil
end
end
end
end
return results
end
--
-- Extended cargo functions
--
function m_cargo.store_from_lua(args)
-- Factory for function that stores data from lua data into a cargo table from a template call
--
-- Arguments:
-- module: Name of the module where the data is located, without the module prefix
-- tables: Mapping of the table data
--
-- Return:
-- function that takes frame argument
--
--
-- The function created takes the following arguments:
-- REQURIED:
-- tbl: table to store
-- src: source wiki path after the module if it differs from the table name
-- index_start: Starting index (default: 1)
-- index_end: Ending index (default: data length, i.e. all data)
args = args or {}
if args.module == nil or args.tables == nil then
error(i18n.errors.store_from_lua_missing_arguments)
end
return function (frame)
-- Get args
tpl_args = getArgs(frame, {
parentFirst = true
})
frame = m_util.misc.get_frame(frame)
if args.tables[tpl_args.tbl] == nil then
error(string.format(i18n.errors.store_from_lua_invalid_table, tostring(tpl_args.tbl)))
end
-- mw.loadData has some problems...
local data = require(string.format('%s:%s/%s', i18n.module, args.module, tpl_args.src or tpl_args.tbl))
tpl_args.index_start = math.max(tonumber(tpl_args.index_start) or 1, 1)
tpl_args.index_end = math.min(tonumber(tpl_args.index_end) or #data, #data)
for i=tpl_args.index_start, tpl_args.index_end do
local row = data[i]
if row == nil then
break
end
-- get full table name
row._table = args.tables[tpl_args.tbl].table
m_cargo.store(frame, row)
end
return string.format(i18n.tooltips.store_rows, tpl_args.index_start, tpl_args.index_end, tpl_args.index_end-tpl_args.index_start+1, tpl_args.tbl)
end
end
--[[test = {
tables = {
},
{
arg = {'argument1', 'argument2'},
header = 'Table header',
fields = {'mods.granted_skill'},
display = function(tpl_args, frame, tr, data)
tr
:tag('td')
:wikitext(data['mods.granted_skill'])
end,
order = 1000,
sort_type = 'text',
options = '',
},
}
=p.table_query{
tpl_args={
test=true,
q_where='mods.generation_type = 5 AND mods.domain = 1',
q_tables='spawn_weights',
q_join='mods._pageID=spawn_weights._pageID',
},
frame=nil,
main_table='mods',
--unique_row_fields={},
--empty_cell
data = {
tables = {
mod_stats = {join = 'mods._pageID=mod_stats._pageID'},
},
{
args = {'test'},
header = 'Id',
fields = {'mods.id'},
display = function(tpl_args, frame, tr, rows, rowinfo)
tr
:tag('td')
:wikitext(rows[1]['mods.id'])
end,
--order = 0,
sort_type = 'text',
--options = {},
},
{
args = {'test'},
header = 'Stats',
fields = {'mod_stats.id', 'mod_stats.min', 'mod_stats.max'},
display = function(tpl_args, frame, tr, rows, rowinfo)
local stats = {}
for _, row in ipairs(rows) do
stats[#stats+1] = string.format('%s: %s to %s', row['mod_stats.id'], row['mod_stats.min'], row['mod_stats.max'])
end
tr
:tag('td')
:wikitext(table.concat(stats, '<br>'))
end,
--order = 0,
sort_type = 'text',
--options = {},
},
},
}
]]
function m_cargo.table_query(args)
-- REQUIRED
-- tpl_args
-- frame
-- main_table
-- data
-- tables
-- [...]
-- args
-- header
-- fields
-- display
-- order
-- sort_type
-- options
-- [...]
-- OPTIONAL
-- row_unique_fields
-- empty_cell
-- table_css
-- TPL_ARGS:
-- q_***
-- default
-- before
-- after
-- *** - as defined in data
local tpl_args = args.tpl_args
local frame = m_util.misc.get_frame(args.frame)
args.data.tables = args.data.tables or {}
args.row_unique_fields = args.row_unique_fields or {string.format('%s._pageID', args.main_table)}
args.empty_cell = args.empty_cell or '<td></td>'
local row_infos = {}
for _, row_info in ipairs(args.data) do
local enabled = false
if row_info.args == nil then
enabled = true
elseif type(row_info.args) == 'string' and m_util.cast.boolean(tpl_args[row_info.args]) then
enabled = true
elseif type(row_info.args) == 'table' then
for _, argument in ipairs(row_info.args) do
if m_util.cast.boolean(tpl_args[argument]) then
enabled = true
break
end
end
end
if enabled then
row_info.options = row_info.options or {}
row_infos[#row_infos+1] = row_info
end
end
-- sort the rows
table.sort(row_infos, function (a, b)
return (a.order or 0) < (b.order or 0)
end)
-- Set tables
local tables_assoc = {
[args.main_table] = true,
}
if tpl_args.q_tables then
for _, tbl_name in ipairs(m_util.string.split(tpl_args.q_tables, ',%s*')) do
tables_assoc[tbl_name] = true
end
end
-- Set required fields
local fields_assoc = {
[string.format('%s._pageID', args.main_table)] = true,
}
for _, rowinfo in ipairs(row_infos) do
if type(rowinfo.fields) == 'function' then
rowinfo.fields = rowinfo.fields()
end
for index, field in ipairs(rowinfo.fields) do
rowinfo.options[index] = rowinfo.options[index] or {}
-- Support using functions such as CONCAT() in fields:
local f = string.match(
field, m_util.string.pattern.valid_var_name() .. '%.'
)
if f ~= nil then
tables_assoc[f] = true
end
fields_assoc[field] = true
-- The results from the cargo query will use the aliased field:
field = m_util.string.split(field, '%s*=%s*')
rowinfo.fields[index] = field[2] or field[1]
end
end
for _, field in ipairs(args.row_unique_fields) do
fields_assoc[field] = true
end
-- Parse query arguments
local query = {
}
for key, value in pairs(tpl_args) do
if string.sub(key, 0, 2) == 'q_' then
query[string.sub(key, 3)] = value
end
end
if tpl_args.q_fields then
local _extra_fields = m_util.string.split_outer(
tpl_args.q_fields,
',%s*',
{'%(', '%)'}
)
for _, field in ipairs(_extra_fields) do
fields_assoc[field] = true
end
end
--
-- Query
--
local tables = {args.main_table}
local joins = {}
for tbl_name, _ in pairs(tables_assoc) do
args.data.tables[tbl_name] = args.data.tables[tbl_name] or {}
if args.data.tables[tbl_name].join then
joins[#joins+1] = args.data.tables[tbl_name].join
tables[#tables+1] = tbl_name
elseif string.match(tpl_args.q_join or '', '.*' .. tbl_name .. '%..*') ~= nil then
tables[#tables+1] = tbl_name
elseif tbl_name ~= args.main_table then
error(string.format(i18n.errors.no_join, tbl_name))
end
end
local fields = {}
for field, _ in pairs(fields_assoc) do
fields[#fields+1] = field
end
if #joins > 0 then
if query.join then
query.join = query.join .. ',' .. table.concat(joins, ',')
else
query.join = table.concat(joins, ',')
end
end
local results = {}
local results_order = {}
local cur_results = m_cargo.query(
tables,
fields,
query
)
for _, row in ipairs(cur_results) do
local unique_key = {}
for _, field_name in ipairs(args.row_unique_fields) do
if row[field_name] == nil then
error(string.format(i18n.errors.missing_unique_field_in_result_row, field_name, string.gsub(mw.dumpObject(row), '\n', '<br>')))
end
unique_key[#unique_key+1] = row[field_name]
end
unique_key = table.concat(unique_key, '__')
if results[unique_key] then
table.insert(results[unique_key], row)
else
results[unique_key] = {row}
results_order[#results_order+1] = unique_key
end
end
if #results_order == 0 then
if tpl_args.default ~= nil then
return tpl_args.default
else
return i18n.errors.no_results
end
end
--
-- Display
--
-- Preformance optimization
if tpl_args.q_fields then
tpl_args._extra_fields = m_util.string.split_outer(
tpl_args.q_fields,
',%s*',
{'%(', '%)'}
)
for index, field in ipairs(tpl_args._extra_fields) do
field = m_util.string.split(field, '%s*=%s*')
-- field[2] will be nil if there is no alias
tpl_args._extra_fields[index] = field[2] or field[1]
end
else
tpl_args._extra_fields = {}
end
local tbl = mw.html.create('table')
tbl:attr('class', 'wikitable sortable ' .. (args.table_css or ''))
-- Header
local tr = tbl:tag('tr')
for _, row_info in ipairs(row_infos) do
tr
:tag('th')
:attr('data-sort-type', row_info.sort_type or 'number')
:wikitext(row_info.header)
:done()
end
for _, field in ipairs(tpl_args._extra_fields) do
tr
:tag('th')
:wikitext(field)
end
-- Body
for _, unique_key in ipairs(results_order) do
local rows = results[unique_key]
tr = tbl:tag('tr')
for _, rowinfo in ipairs(row_infos) do
local display_fields = {}
for index, field in ipairs(rowinfo.fields) do
if rowinfo.options[index].optional ~= true then
display_fields[field] = false
for _, row in ipairs(rows) do
if row[field] ~= nil then
display_fields[field] = true
break
end
end
end
end
local display = true
for key, value in pairs(display_fields) do
if not value then
display = false
break
end
end
if display then
rowinfo.display(tpl_args, frame, tr, rows, rowinfo)
else
tr:wikitext(args.empty_cell)
end
end
-- Add extra columns specified by tpl_args.q_fields:
for _, field in ipairs(tpl_args._extra_fields) do
local extra_col = {}
for _, row in ipairs(rows) do
if row[field] then
extra_col[#extra_col+1] = row[field]
end
end
if #extra_col > 0 then
tr
:tag('td')
:wikitext(table.concat(extra_col, '<br>'))
else
tr:wikitext(args.empty_cell)
end
end
end
return (tpl_args.before or '') .. tostring(tbl) .. (tpl_args.after or '')
end
function m_cargo.parse_field_arguments(args)
-- Maps the arguments from a cargo argument table (i.e. the ones used in m_cargo.declare_factory)
--
-- It will expect/handle the following fields:
-- map.order - REQUIRED - Array table for the order in which the arguments in map.fields will be parsed
-- map.table - REQUIRED - Table name (for storage)
-- map.fields[id].field - REQUIRED - Name of the field in cargo table
-- map.fields[id].type - REQUIRED - Type of the field in cargo table
-- map.fields[id].func - OPTIONAL - Function to handle the arguments. It will be passed tpl_args and frame.
-- The function should return the parsed value.
--
-- If no function is specified, default handling depending on the cargo field type will be used
-- map.fields[id].default - OPTIONAL - Default value if the value is not set or returned as nil
-- If default is a function, the function will be called with (tpl_args, frame) and expected to return a default value for the field.
-- map.fields[id].name - OPTIONAL - Name of the field in tpl_args if it differs from the id in map.fields. Used for i18n for example
-- map.fields[id].required - OPTIONAL - Whether a value for the field is required or whether it can be left empty
-- Note: With a default value the field will never be empty
-- map.fields[id].skip - OPTIONAL - Skip field if missing from order
--
--
-- Expects argument table.
-- REQUIRED:
-- tpl_args - arguments passed to template after preprecessing
-- frame - frame object
-- table_map - table mapping object
-- rtr - if set return cargo props instead of storing them
local tpl_args = args.tpl_args
local frame = args.frame
local map = args.table_map
local cargo_values = {_table = map.table}
-- for checking missing keys in order
local available_fields = {}
for key, field in pairs(map.fields) do
if field.skip == nil then
available_fields[key] = true
end
end
-- main loop
for _, key in ipairs(map.order) do
local field = map.fields[key]
if field == nil then
error(string.format(i18n.errors.missing_key_in_fields, key, map.table))
else
available_fields[key] = nil
end
-- key in argument mapping
local args_key
if field.name then
args_key = field.name
else
args_key = key
end
-- Retrieve value
local value
-- automatic handling only works if the field type is set
if field.type ~= nil then
value = tpl_args[args_key]
local cfield = m_cargo.parse_field{field=field.type}
local handler
if cfield.type == 'Integer' or cfield.type == 'Float' then
handler = tonumber
elseif cfield.type == 'Boolean' then
handler = function (value)
return m_util.cast.boolean(value, {cast_nil=false})
end
end
if cfield.list and value ~= nil then
-- ingore whitespace between separator and values
value = m_util.string.split(value, cfield.list .. '%s*')
if handler then
for index, v in ipairs(value) do
value[index] = handler(v)
if value[index] == nil then
error(string.format(i18n.errors.handler_returned_nil, map.table, args_key, v, field.type))
end
end
end
elseif handler and value ~= nil then
value = handler(value)
if value == nil then
error(string.format(i18n.errors.handler_returned_nil, map.table, args_key, tpl_args[args_key], field.type))
end
end
-- Don't need special handling: String, Text, Wikitext, Searchtext
-- Consider: Page, Date, Datetime, Coordinates, File, URL, Email
end
if field.func ~= nil then
value = field.func(tpl_args, frame, value)
end
-- Check defaults
if value == nil and field.default ~= nil then
if type(field.default) == 'function' then
value = field.default(tpl_args, frame)
elseif type(field.default) == 'table' then
mw.logObject(string.format(i18n.errors.table_object_as_default, key, map.table))
value = mw.clone(field.default)
else
value = field.default
end
end
-- Add value to arguments and cargo data
if value ~= nil then
-- key will be used here since the value will be used internally from here on in english
tpl_args[key] = value
if field.field ~= nil then
cargo_values[field.field] = value
end
elseif field.required == true then
error(string.format(i18n.errors.argument_required, args_key))
end
end
-- check for missing keys and return error if any are missing
local missing = {}
for key, _ in pairs(available_fields) do
missing[#missing+1] = key
end
if #missing > 0 then
error(string.format(i18n.errors.missing_key_in_order, map.table, table.concat(missing, '\n')))
end
-- finally store data in DB
if args.rtr ~= nil then
return cargo_values
else
m_cargo.store(frame, cargo_values)
end
end
function m_cargo.declare_factory(args)
-- Returns a function that can be called by templates to declare cargo tables
--
-- args
-- data: data table
-- table: name of cargo table
-- fields: associative table with:
-- field: name of the field to declare
-- type: type of the field
return function (frame)
frame = m_util.misc.get_frame(frame)
local dcl_args = {}
dcl_args._table = args.data.table
for k, field_data in pairs(args.data.fields) do
if field_data.field then
dcl_args[field_data.field] = field_data.type
end
end
return m_cargo.declare(frame, dcl_args)
end
end
function m_cargo.attach_factory(args)
-- Returns a function that can be called by templates to attach cargo tables
--
-- args
-- data: data table
-- table: name of cargo table
-- fields: associative table with:
-- field: name of the field to declare
-- type: type of the field
return function (frame)
frame = m_util.misc.get_frame(frame)
local attach_args = {}
attach_args._table = args.data.table
return m_cargo.attach(frame, attach_args)
end
end
-- mw.logObject(m_cargo.map_results_to_id{results=mw.ext.cargo.query('mods,spawn_weights', 'mods._pageID, spawn_weights.tag', {where='mods.id="Strength1"', join='mods._pageID=spawn_weights._pageID'}), field='mods._pageID'})
function m_cargo.map_results_to_id(args)
-- Maps the results passed to a table containing the specified field as key and a table of rows for the particular page as values.
--
-- args
-- results : Table of results returned from mw.ext.cargo.query to map to the specified id field.
-- field : Name of the id field to map results to
-- the field has to be in the fields list of the original query or it will cause errors.
-- keep_id_field : If set then don't delete _pageID.
-- append_id_field : If set then append the id to the table sequentially as well which allows preserving
-- the id order they were found in.
--
-- return
-- table
-- key : The specified id field
-- value : Array containing the found rows (in the order that they were found)
local out = {}
for _, row in ipairs(args.results) do
local key = row[args.field]
if out[key] then
out[key][#out[key]+1] = row
else
out[key] = {row}
-- Append the ids sequentially, this allows preserving the order
-- the ids were found:
if args.append_id_field ~= nil then
out[#out+1] = key
end
end
--Discard the pageID, don't need this any longer in most cases:
if args.keep_id_field == nil then
row[args.field] = nil
end
end
return out
end
function m_cargo.array_query(args)
-- Performs a long "OR" query from the given array and field validating that there is only exactly one match returned
--
-- args:
-- REQUIRED:
-- tables - array of tables (see m_cargo.query)
-- fields - array of fields (see m_cargo.query)
-- id_array - list of ids to query for
-- id_field - name of the id field, will be automatically added to fields
-- OPTIONAL:
-- query - array containing cargo sql clauses [optional] (see m_cargo.query)
-- ingore_missing - skip the check for missing fields entirely
-- warning_on_missing - issue warning instead of error if missing values
--
-- RETURN:
-- table - results as given by mw.ext.cargo.query
-- msg - any error messages if it was used as warning
args.query = args.query or {}
args.fields[#args.fields+1] = args.id_field
if #args.id_array == 0 then
return {}
end
-- remove blanks
local id_array = {}
for _, value in ipairs(args.id_array) do
if value ~= '' then
id_array[#id_array+1] = value
end
end
-- for error returning
local msg = {}
local where = string.format('%s IN ("%s")', args.id_field, table.concat(id_array, '","'))
if args.query.where then
args.query.where = string.format('(%s) AND (%s)', args.query.where, where)
else
args.query.where = where
end
--
-- Prepare query
--
local results = m_cargo.query(
args.tables,
args.fields,
args.query
)
--
-- Check missing results
--
if #results ~= #id_array then
--
-- Check for duplicates
--
-- The usage of distinct should elimate duplicates here from cargo being bugged while still showing actual data duplicates.
local dupes = m_cargo.query(
args.tables,
{
string.format('COUNT(DISTINCT %s._pageID)=count', args.tables[1]),
args.id_field,
},
{
join=args.query.join,
where=args.query.where,
groupBy=args.id_field,
having=string.format('COUNT(DISTINCT %s._pageID) > 1', args.tables[1]),
}
)
if #dupes > 0 then
out = {}
for _, row in ipairs(dupes) do
out[#out+1] = string.format('%s (%s pages found)', row[args.id_field], row['count'])
end
error(string.format(i18n.errors.duplicate_ids, args.id_field, table.concat(out, '\n')))
end
local dupes = m_cargo.query(
args.tables,
{
string.format('COUNT(%s)=count', args.id_field),
string.format('%s._pageName=page', args.tables[1]),
},
{
join=args.query.join,
where=args.query.where,
groupBy=string.format('%s._pageName', args.tables[1]),
having=string.format('COUNT(%s) > 1', args.id_field),
}
)
if #dupes > 0 then
out = {}
for _, row in ipairs(dupes) do
out[#out+1] = string.format('"%s" (%s entries found)', row['page'], row['count'])
end
error(string.format(i18n.errors.duplicate_ids, args.id_field, table.concat(out, '\n')))
end
if not args.ignore_missing then
local missing = {}
for _, id in ipairs(id_array) do
missing[id] = true
end
for _, row in ipairs(results) do
missing[row[args.id_field]] = nil
end
local missing_ids = {}
for k, _ in pairs(missing) do
missing_ids[#missing_ids+1] = k
end
msg[#msg+1] = string.format(i18n.errors.missing_ids, args.id_field, table.concat(missing_ids, '\n'))
if args.warning_on_missing == nil then
error(msg[#msg])
else
mw.logObject(msg[#msg])
end
end
end
return results, msg
end
function m_cargo.replace_holds(args)
-- Replaces a holds query with a like or regexp equivalent.
--
-- required args:
-- string: string to replace
--
-- optional args:
-- mode : Either "like" or "regex"; default "regex"
-- like: Replaces the holds query with a LIKE equivalent
-- regex: Replaces the holds query with a REGEXP equivalent
-- field : Field pattern to use. Can be used to restrict the hold replacement to specific fields.
-- Default: all fields are matched.
-- separator: Separator for field entries to use in the REGEXP mode.
-- Default: ,
--
-- Returns the replaced query
local args = args or {}
-- if the field is not specified, replace any holds query
args.field = args.field or '[%w_\.]+'
if args.mode == 'like' or args.mode == nil then
return string.gsub(
args.string,
string.format('(%s) HOLDS ([NOT ]*)([LIKE ]*)"([^"]+)"', args.field),
'%1__full %2LIKE "%%%4%%"'
)
elseif args.mode == 'regex' then
args.separator = args.separator or ','
return string.gsub(
args.string,
string.format('(%s) HOLDS ([NOT ]*)"([^"]+)"', args.field),
string.format('%%1__full %%2REGEXP "(%s|^)%%3(%s|$)"', args.separator, args.separator)
)
else
error('Invalid mode specified. Acceptable values are like or regex.')
end
end
function m_cargo.parse_field(args)
-- Parse a cargo field declaration and returns a table containing the results
--
-- required args:
-- field: field to parse
--
-- Return
-- type - Type of the field
-- list - Separator of the list if it is a list type field
-- parameters - any parameters to the field itself
local field = args.field
local results = {}
local match
match = { string.match(field, 'List %(([^%(%)]+)%) of (.*)') }
if #match > 0 then
results.list = match[1]
field = match[2]
end
match = { string.match(field, '%s*(%a+)%s*%(([^%(%)]+)%)') }
if #match > 0 then
results.type = match[1]
field = match[2]
results.parameters = {}
for _, param_string in ipairs(m_util.string.split(field, ';')) do
local index = { string.find(param_string, '=') }
local key
local value
if #index > 0 then
key = string.sub(param_string, 0, index[1]-1)
value = m_util.string.strip(string.sub(param_string, index[1]+1))
else
key = param_string
value = true
end
results.parameters[m_util.string.strip(key)] = value
end
else
-- the reminder must be the type since there is no extra declarations
results.type = string.match(field, '%s*(%a+)%s*')
end
return results
end
return m_cargo