diff --git a/README.md b/README.md index 9acaeca..02f49f2 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ There are a few parsing options available that are passed in the the `options` p tinytoml.parse("a=2024-10-31T12:49:00Z", {load_from_string=true, type_conversion=type_conversion}) ``` -- `encode_datetime_as` (default `string`) +- `parse_datetime_as` (default `string`) Allows encoding datetime either as a `string` or a `table`. The `table` will take all the individual fields and place them in a table. This can be used in conjunction with `type_conversion` - either the string or table representation would be passed into whatever function is @@ -88,14 +88,15 @@ There are a few parsing options available that are passed in the the `options` p ``` ```lua - -- with the option: { encode_datetime_as = "string" } + -- with the option: { parse_datetime_as = "string" } { offset_datetime = "1979-05-27T07:32:00Z", local_datetime = "1979-05-27T07:32:00", local_time = "07:32:00", local_date = "1979-05-27" } - -- with the option: { encode_datetime_as = "table" } + + -- with the option: { parse_datetime_as = "table" } { offset_datetime = {year = 1979, month = 05, day = 27, hour = 7, min = 32, sec = 0, msec = 0, time_offset = "00:00"}, local_datetime = {year = 1979, month = 05, day = 27, hour = 7, min = 32, sec = 0, msec = 0}, diff --git a/tests/date_testing.tl b/tests/date_testing.tl new file mode 100644 index 0000000..f412d2c --- /dev/null +++ b/tests/date_testing.tl @@ -0,0 +1,44 @@ +local tested = require("tested") +local tinytoml = require("tinytoml") + +tested.test("encode date as string", function() + + local date_toml = [[offset_datetime = 1979-05-27T07:32:00Z +local_datetime = 1979-05-27T07:32:00 +local_time = 07:32:00 +local_date = 1979-05-27]] + + local expected = { + offset_datetime = "1979-05-27T07:32:00Z", + local_datetime = "1979-05-27T07:32:00", + local_time = "07:32:00", + local_date = "1979-05-27" + } + + local parsed_dates = tinytoml.parse(date_toml, {load_from_string=true, parse_datetime_as="string"}) + + tested.assert({given="toml with dates", should="parse dates as strings", expected=expected, actual=parsed_dates}) + +end) + +tested.test("encode date as table", function() + + local date_toml = [[offset_datetime = 1979-05-27T07:32:00Z +local_datetime = 1979-05-27T07:32:00 +local_time = 07:32:00 +local_date = 1979-05-27]] + + local expected = { + offset_datetime = {year = 1979, month = 05, day = 27, hour = 7, min = 32, sec = 0, msec = 0, time_offset = "00:00"}, + local_datetime = {year = 1979, month = 05, day = 27, hour = 7, min = 32, sec = 0, msec = 0}, + local_time = {hour = 7, min = 32, sec = 0, msec = 0}, + local_date = {year = 1979, month = 05, day = 27} + } + + local parsed_dates = tinytoml.parse(date_toml, {load_from_string=true, parse_datetime_as="table"}) + + tested.assert({given="toml with dates", should="parse dates as tables", expected=expected, actual=parsed_dates}) + +end) + +return tested \ No newline at end of file diff --git a/tests/max_nesting_depth.tl b/tests/max_nesting_depth.tl new file mode 100644 index 0000000..a68db64 --- /dev/null +++ b/tests/max_nesting_depth.tl @@ -0,0 +1,54 @@ +local tinytoml = require("tinytoml") +local tested = require("tested") + +tested.test("max nesting depth", function() + local crazy_toml = {"e="} + for i = 1, 2000 do + table.insert(crazy_toml, "{e=") + end + + table.insert(crazy_toml, "{}") + for i = 1, 2000 do + table.insert(crazy_toml, "}") + end + + local ok, err = pcall(tinytoml.parse, table.concat(crazy_toml), {load_from_string=true}) + + tested.assert({given="too much nesting", should="raies an error", expected=false, actual=ok}) + tested.assert({given="too much nesting", should="error includes max_nesting_depth", expected=true, actual=err:find("'max_nesting_depth'") ~= nil}) + +end) + +tested.test("at max nesting depth", function() + local crazy_toml = {"e="} + for i = 1, 998 do + table.insert(crazy_toml, "{e=") + end + + table.insert(crazy_toml, "{}") + for i = 1, 998 do + table.insert(crazy_toml, "}") + end + + local ok, err = pcall(tinytoml.parse, table.concat(crazy_toml), {load_from_string=true}) + + tested.assert({given="edge of nesting", should="does not raise an error", expected=true, actual=ok}) +end) + +tested.test("slightly above max nesting depth", function() + local crazy_toml = {"e="} + for i = 1, 1500 do + table.insert(crazy_toml, "{e=") + end + + table.insert(crazy_toml, "{}") + for i = 1, 1500 do + table.insert(crazy_toml, "}") + end + + local ok, err = pcall(tinytoml.parse, table.concat(crazy_toml), {load_from_string=true, max_nesting_depth=2000}) + + tested.assert({given="edge of nesting", should="does not raise an error", expected=true, actual=ok}) +end) + +return tested \ No newline at end of file diff --git a/tinytoml.lua b/tinytoml.lua index 1a314bd..ef81455 100644 --- a/tinytoml.lua +++ b/tinytoml.lua @@ -633,7 +633,7 @@ end local function assign_time_local(sm, match, hour, min, sec, msec) sm.value_type = "time-local" - if sm.options.encode_datetime_as == "string" then + if sm.options.parse_datetime_as == "string" then sm.value = sm.options.type_conversion[sm.value_type](match) else sm.value = sm.options.type_conversion[sm.value_type]({ hour = hour, min = min, sec = sec, msec = msec }) @@ -642,7 +642,7 @@ end local function assign_date_local(sm, match, year, month, day) sm.value_type = "date-local" - if sm.options.encode_datetime_as == "string" then + if sm.options.parse_datetime_as == "string" then sm.value = sm.options.type_conversion[sm.value_type](match) else sm.value = sm.options.type_conversion[sm.value_type]({ year = year, month = month, day = day }) @@ -651,7 +651,7 @@ end local function assign_datetime_local(sm, match, year, month, day, hour, min, sec, msec) sm.value_type = "datetime-local" - if sm.options.encode_datetime_as == "string" then + if sm.options.parse_datetime_as == "string" then sm.value = sm.options.type_conversion[sm.value_type](match) else sm.value = sm.options.type_conversion[sm.value_type]({ year = year, month = month, day = day, hour = hour, min = min, sec = sec, msec = msec or 0 }) @@ -665,7 +665,7 @@ local function assign_datetime(sm, match, year, month, day, hour, min, sec, msec validate_hours_minutes(sm, _tointeger(hour_s), _tointeger(min_s), "offset-date-time") end sm.value_type = "datetime" - if sm.options.encode_datetime_as == "string" then + if sm.options.parse_datetime_as == "string" then sm.value = sm.options.type_conversion[sm.value_type](match) else sm.value = sm.options.type_conversion[sm.value_type]({ year = year, month = month, day = day, hour = hour, min = min, sec = sec, msec = msec or 0, time_offset = tz or "00:00" }) @@ -739,7 +739,8 @@ local function validate_datetime(sm, value) local temp_ext sm._, sm._, sec_s, temp_ext = sm.ext:find("^:(%d%d)(.*)$") if sec_s then - validate_seconds(sm, _tointeger(sec_s), "local-time") + sec = _tointeger(sec_s) + validate_seconds(sm, sec, "local-time") sm.match = sm.match .. ":" .. sec_s sm.ext = temp_ext else @@ -1162,7 +1163,7 @@ function tinytoml.parse(filename, options) max_nesting_depth = 1000, max_filesize = 100000000, load_from_string = false, - encode_datetime_as = "string", + parse_datetime_as = "string", type_conversion = { ["datetime"] = generic_type_conversion, ["datetime-local"] = generic_type_conversion, @@ -1185,8 +1186,8 @@ function tinytoml.parse(filename, options) assert(type(options.load_from_string) == "boolean", "the tinytoml option 'load_from_string' takes in a 'function'. You passed in the value '" .. tostring(options.load_from_string) .. "' of type '" .. type(options.load_from_string) .. "'") end - if options.encode_datetime_as ~= nil then - assert(type(options.encode_datetime_as) == "string", "the tinytoml option 'encode_datetime_as' takes in either the 'string' or 'table' (as type 'string'). You passed in the value '" .. tostring(options.encode_datetime_as) .. "' of type '" .. type(options.encode_datetime_as) .. "'") + if options.parse_datetime_as ~= nil then + assert(type(options.parse_datetime_as) == "string", "the tinytoml option 'parse_datetime_as' takes in either the 'string' or 'table' (as type 'string'). You passed in the value '" .. tostring(options.parse_datetime_as) .. "' of type '" .. type(options.parse_datetime_as) .. "'") end if options.type_conversion ~= nil then @@ -1204,7 +1205,7 @@ function tinytoml.parse(filename, options) options.max_nesting_depth = options.max_nesting_depth or default_options.max_nesting_depth options.max_filesize = options.max_filesize or default_options.max_filesize options.load_from_string = options.load_from_string or default_options.load_from_string - options.encode_datetime_as = options.encode_datetime_as or default_options.encode_datetime_as + options.parse_datetime_as = options.parse_datetime_as or default_options.parse_datetime_as options.type_conversion = options.type_conversion or default_options.type_conversion @@ -1351,7 +1352,7 @@ local function escape_string(str, multiline, is_key) ["date-local"] = generic_type_conversion, ["time-local"] = generic_type_conversion, } - sm.options.encode_datetime_as = "string" + sm.options.parse_datetime_as = "string" sm._, sm.end_seq, sm.match = sm.input:find("^([^ #\r\n,%[{%]}]+)", sm.i) diff --git a/tinytoml.tl b/tinytoml.tl index c8fb2d6..03f5068 100644 --- a/tinytoml.tl +++ b/tinytoml.tl @@ -13,7 +13,7 @@ end local record TinyTomlOptions load_from_string: boolean type_conversion: {TomlConversionType:function(raw_value: string | table):T} - encode_datetime_as: DateTimeEncode + parse_datetime_as: DateTimeEncode max_filesize: integer max_nesting_depth: integer end @@ -633,7 +633,7 @@ end local function assign_time_local(sm: StateMachine, match: string, hour: integer, min: integer, sec: integer, msec: integer) sm.value_type = "time-local" - if sm.options.encode_datetime_as == "string" then + if sm.options.parse_datetime_as == "string" then sm.value = sm.options.type_conversion[sm.value_type as TomlConversionType](match) else sm.value = sm.options.type_conversion[sm.value_type as TomlConversionType]({hour=hour, min=min, sec=sec, msec=msec}) @@ -642,7 +642,7 @@ end local function assign_date_local(sm: StateMachine, match: string, year: integer, month: integer, day: integer) sm.value_type = "date-local" - if sm.options.encode_datetime_as == "string" then + if sm.options.parse_datetime_as == "string" then sm.value = sm.options.type_conversion[sm.value_type as TomlConversionType](match) else sm.value = sm.options.type_conversion[sm.value_type as TomlConversionType]({year=year, month=month, day=day}) @@ -651,7 +651,7 @@ end local function assign_datetime_local(sm: StateMachine, match: string, year: integer, month: integer, day: integer, hour: integer, min: integer, sec: integer, msec?: integer) sm.value_type = "datetime-local" - if sm.options.encode_datetime_as == "string" then + if sm.options.parse_datetime_as == "string" then sm.value = sm.options.type_conversion[sm.value_type as TomlConversionType](match) else sm.value = sm.options.type_conversion[sm.value_type as TomlConversionType]({year=year, month=month, day=day, hour=hour, min=min, sec=sec, msec=msec or 0}) @@ -665,7 +665,7 @@ local function assign_datetime(sm: StateMachine, match: string, year: integer, m validate_hours_minutes(sm, _tointeger(hour_s), _tointeger(min_s), "offset-date-time") end sm.value_type = "datetime" - if sm.options.encode_datetime_as == "string" then + if sm.options.parse_datetime_as == "string" then sm.value = sm.options.type_conversion[sm.value_type as TomlConversionType](match) else sm.value = sm.options.type_conversion[sm.value_type as TomlConversionType]({year=year, month=month, day=day, hour=hour, min=min, sec=sec, msec=msec or 0, time_offset=tz or "00:00"}) @@ -739,7 +739,8 @@ local function validate_datetime(sm: StateMachine, value: string): boolean local temp_ext: string sm._, sm._, sec_s, temp_ext = sm.ext:find("^:(%d%d)(.*)$") as (integer, integer, string, string) if sec_s then - validate_seconds(sm, _tointeger(sec_s), "local-time") + sec = _tointeger(sec_s) + validate_seconds(sm, sec, "local-time") sm.match = sm.match .. ":" .. sec_s sm.ext = temp_ext else @@ -1162,7 +1163,7 @@ function tinytoml.parse(filename: string, options?: TinyTomlOptions): {string:an max_nesting_depth = 1000, max_filesize = 100000000, load_from_string = false, - encode_datetime_as = "string", + parse_datetime_as = "string", type_conversion = { ["datetime"] = generic_type_conversion, ["datetime-local"] = generic_type_conversion, @@ -1185,8 +1186,8 @@ function tinytoml.parse(filename: string, options?: TinyTomlOptions): {string:an assert(type(options.load_from_string) == "boolean", "the tinytoml option 'load_from_string' takes in a 'function'. You passed in the value '" .. tostring(options.load_from_string) .. "' of type '" .. type(options.load_from_string) .. "'") end - if options.encode_datetime_as ~= nil then - assert(type(options.encode_datetime_as) == "string", "the tinytoml option 'encode_datetime_as' takes in either the 'string' or 'table' (as type 'string'). You passed in the value '" .. tostring(options.encode_datetime_as) .. "' of type '" .. type(options.encode_datetime_as) .. "'") + if options.parse_datetime_as ~= nil then + assert(type(options.parse_datetime_as) == "string", "the tinytoml option 'parse_datetime_as' takes in either the 'string' or 'table' (as type 'string'). You passed in the value '" .. tostring(options.parse_datetime_as) .. "' of type '" .. type(options.parse_datetime_as) .. "'") end if options.type_conversion ~= nil then @@ -1204,7 +1205,7 @@ function tinytoml.parse(filename: string, options?: TinyTomlOptions): {string:an options.max_nesting_depth = options.max_nesting_depth or default_options.max_nesting_depth options.max_filesize = options.max_filesize or default_options.max_filesize options.load_from_string = options.load_from_string or default_options.load_from_string - options.encode_datetime_as = options.encode_datetime_as or default_options.encode_datetime_as + options.parse_datetime_as = options.parse_datetime_as or default_options.parse_datetime_as options.type_conversion = options.type_conversion or default_options.type_conversion -- verify/setup the last couple of things @@ -1351,7 +1352,7 @@ local function escape_string(str: string, multiline: boolean, is_key: boolean): ["date-local"] = generic_type_conversion, ["time-local"] = generic_type_conversion } - sm.options.encode_datetime_as = "string" + sm.options.parse_datetime_as = "string" -- performing the same setup as in close_other_value, should set values correctly to run validate_datetime sm._, sm.end_seq, sm.match = sm.input:find("^([^ #\r\n,%[{%]}]+)", sm.i)