Feel.Value.Format (feel v0.1.0)

Copy Markdown View Source

The text of a value, as string(...) writes it (DMN 1.6, section 10.3.4.1; D-56).

The specification gives one example, string(1.1) is "1.1", and says a date, time and duration are written in ISO 8601 (notes 06, AE-16); the TCK (model 0079-feel-string-function, and the many string(...) of the others) has the rest.

ValueText
a stringitself
a numberits digits, with its scale and no exponent: 123.45
a booleantrue, false
a date2018-12-10; a year has at least four digits (0044, 99999), and - for a year before 0
a time10:30:00.0001, and its zone: Z for an offset of 0, +05:00 and -02:45:55 for another (the seconds only when it has some), @Europe/Paris for a zone id
a date and timethe date, T, and the time: 2018-12-10T10:30:00@Etc/UTC
a days and time durationP1DT2H3M4.1234S, with the parts that are not zero; PT0S for none
a years and months durationP2Y1M, with the parts that are not zero; P0M for none
a list[1, 2, 3, "foo"]: its elements, written as FEEL literals
a context{a: "foo", b: {bar: "baz"}}: its entries in order (D-135), a key that is not a plain name in quotes
a range[18..21): a range literal (grammar rule 66), endpoints written as FEEL literals; an end that is not there is left out, with an open bracket: [2..), (..2] (D-73)

An element of a list or a context is written as a FEEL literal, so that the text could be read back: a string is in quotes (with \ and \" escaped), null is null, and a date, a time, a date and time or a duration is an @"..." literal. A function has no text yet.

iex> Feel.Value.Format.string(Decimal.new("123.45"))
"123.45"
iex> Feel.Value.Format.string([Decimal.new(1), "foo", true, nil])
~s([1, "foo", true, null])
iex> Feel.Value.Format.string(%{"a" => "foo"})
~s({a: "foo"})
iex> Feel.Value.Format.string(Feel.Value.Context.new([{"b", Decimal.new(1)}, {"a", "foo"}]))
~s({b: 1, a: "foo"})
iex> {:ok, time} = Feel.Value.Time.parse("10:30:00.0001+05:00:01")
iex> Feel.Value.Format.string(time)
"10:30:00.0001+05:00:01"

Summary

Functions

A date: 2018-12-10. The year has at least four digits, and a year before 0 has a -.

A date and time: the date, T, and the time.

A days and time duration, from its seconds: the days, hours, minutes and seconds that are not zero, and PT0S for none. The seconds keep their fraction, without its zeros at the end.

The text of a value of FEEL, as string(...) writes it. null has none (string(null) is null).

string/1, unless the text is longer than max bytes (:infinity for no cap): then {:over, size}, with the bytes counted when the text passed max, and the rest of it is not written.

A time: the time of day, and the zone if it has one.

A years and months duration, from its months: the years and the months that are not zero, and P0M for none.

Functions

date(date)

@spec date(Date.t()) :: String.t()

A date: 2018-12-10. The year has at least four digits, and a year before 0 has a -.

iex> Feel.Value.Format.date(~D[2018-12-10])
"2018-12-10"
iex> Feel.Value.Format.date(Date.new!(-999_999_999, 12, 31))
"-999999999-12-31"
iex> Feel.Value.Format.date(Date.new!(44, 3, 15))
"0044-03-15"

date_time(date_time)

@spec date_time(Feel.Value.DateTime.t()) :: String.t()

A date and time: the date, T, and the time.

iex> {:ok, date_time} = Feel.Value.DateTime.parse("2011-12-31T10:15:30@Europe/Paris")
iex> Feel.Value.Format.date_time(date_time)
"2011-12-31T10:15:30@Europe/Paris"

days_and_time(number)

@spec days_and_time(Decimal.t()) :: String.t()

A days and time duration, from its seconds: the days, hours, minutes and seconds that are not zero, and PT0S for none. The seconds keep their fraction, without its zeros at the end.

iex> Feel.Value.Format.days_and_time(Decimal.new(93_600))
"P1DT2H"
iex> Feel.Value.Format.days_and_time(Decimal.new("93784.1234"))
"P1DT2H3M4.1234S"
iex> Feel.Value.Format.days_and_time(Decimal.new(0))
"PT0S"

string(value)

@spec string(Feel.Value.t()) :: String.t()

The text of a value of FEEL, as string(...) writes it. null has none (string(null) is null).

A function has no text, and raises RuntimeError: string(...) does not ask for one (D-82), and a list or a context that holds a function has none either.

string_within(value, max)

@spec string_within(Feel.Value.t(), non_neg_integer() | :infinity) ::
  {:ok, String.t()} | {:over, pos_integer()}

string/1, unless the text is longer than max bytes (:infinity for no cap): then {:over, size}, with the bytes counted when the text passed max, and the rest of it is not written.

A list can hold the same string a hundred thousand times, and a context the same list: the text of such a value is as long as the product of their sizes, and a cap that looks at the finished text sees it after it is made. The text is written as a list of pieces and added up as it is, and the walk ends at the first piece over max.

iex> Feel.Value.Format.string_within([Decimal.new(1), "foo"], 100)
{:ok, ~s([1, "foo"])}
iex> Feel.Value.Format.string_within([Decimal.new(1), "foo"], 8)
{:over, 10}
iex> Feel.Value.Format.string_within("foo", 1)
{:ok, "foo"}

time(time)

@spec time(Feel.Value.Time.t()) :: String.t()

A time: the time of day, and the zone if it has one.

iex> {:ok, time} = Feel.Value.Time.parse("23:59:00Z")
iex> Feel.Value.Format.time(time)
"23:59:00Z"
iex> {:ok, time} = Feel.Value.Time.parse("11:59:45-02:45:55")
iex> Feel.Value.Format.time(time)
"11:59:45-02:45:55"

years_and_months(months)

@spec years_and_months(integer()) :: String.t()

A years and months duration, from its months: the years and the months that are not zero, and P0M for none.

iex> Feel.Value.Format.years_and_months(25)
"P2Y1M"
iex> Feel.Value.Format.years_and_months(-12)
"-P1Y"
iex> Feel.Value.Format.years_and_months(0)
"P0M"