]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
validate oddities in casting
authorAlan T. DeKok <aland@freeradius.org>
Thu, 12 Oct 2023 20:16:29 +0000 (16:16 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 13 Oct 2023 16:51:08 +0000 (12:51 -0400)
(string) &foo[*] returnis a list of value-boxes, each cast
to a string

(string) (&foo[*]) return one string, which is all of the
value-boxes concatenated together

and update documentation

doc/antora/modules/reference/pages/type/cast.adoc
src/tests/keywords/concat

index 8be651a0af9b4ed6a6f782c3d267c5130e8f2f3a..771f0096e8a95bdf5fc3371e0c86f95908c76ca5 100644 (file)
@@ -21,16 +21,16 @@ if (%sql("SELECT ipaddress FROM table WHERE user=%{User-Name}") == 192.0.2.1) }
 
 Since there is no attribute reference on either side of the `==`
 operator, the interpreter has no way of knowing that the string
-`192.0.2.1` is an IP address.  There is unfortunately no way of
-_always_ parsing strings in order to automatically determine which
-data type to use.  Any such automatic parsing would work most of the
-time, but it would have error cases where the parsing was incorrect.
+`192.0.2.1` is an IP address.  In most cases, the interpreter will
+guess correctly.  Howerver, there is no way of _always_ parsing
+strings in order to automatically determine which data type to use.
+Any such automatic parsing works most of the time, but it does have
+error cases where the parsing may be incorrect.
 
-The solution is to resolve these ambiguities by allowing the values to
-be cast to a particular type.  Casting a value to a type tells the
-interpreter how that value should be parsed.  Casting is done by
-prefixing a value with the type name, surrounded by brackets;
-`(...)`.
+The solution to resolving these ambiguities is by allowing the values
+to be `cast` to a particular type.  Casting a value to a type tells
+the interpreter how that value should be parsed.  Casting is done by
+prefixing a value with the type name, surrounded by brackets; `(...)`.
 
 .Syntax
 ----
@@ -48,19 +48,19 @@ if (%sql("SELECT ipaddress FROM table WHERE user=%{User-Name}") == (ipaddr)192.0
 ----
 
 In this example, we prefix the IP address with the string `(ipaddr)`.
-The interpreter then knows that the value `192.0.2.` should be
+The interpreter then knows that the value `192.0.2.1` should be
 interpreted as the data type `ipaddr`, and not as the literal string
-`"192.0.2."`.
-
-For a full list of data types which can be used in a cast, please see
-the xref:type/all_types.adoc[list of data types] page, and the
-"Basic Type Types" section.
+`"192.0.2.1"`.
 
 In most cases, the server can automatically determine what data type
 to use.  The cast syntax is used when either the data type is
 ambiguous, or when data should be normalized prior to comparison, or
 when a specific data type is required.
 
+For a full list of data types which can be used in a cast, please see
+the xref:type/all_types.adoc[list of data types] page, and the
+"Basic Type Types" section.
+
 == Casting Behavior
 
 In general, casting data types of different sizes will fail.  For
@@ -111,7 +111,7 @@ received from the network.  For example, casting the `octets` string
 
 Casting any data type to `string` means _printing_ the data type to a
 string, and assigning the resulting value to the `string`.  For a
-value of typ e `octets`, this means that the output is a hex string,
+value of type `octets`, this means that the output is a hex string,
 prefixed with `0x`.  In order to get the "raw" hex values of an
 `octets` data type, the `%hex(...)` expansion is used.  It prints out
 the hex value of its input, without the leading `0x` characters.
@@ -122,6 +122,51 @@ string as that data type.
 See xref:type/string/double.adoc[double-quoted strings] for examples
 of how double-quoted strings are used.
 
+=== Lists
+
+When a cast is applied to a list, the cast is applied _individually to
+each entry in the list_.  This behavior is most noticable when calling
+a function, or looping over a set of attributes.
+
+The following example will first create two `Reply-Message` attributes
+in the request.  It will then copy all of those attributes to the
+reply.
+
+.Example Copying Attributes
+[source,unlang]
+----
+&control.Reply-Message := { "one", "two" }
+
+&reply.Reply-Message := &control.Reply-Message[*]
+----
+
+The following example will take an input string `"192.168.0.1"`, split
+it on the `'.'` character, and then assign it to the `Tmp-Integer-0`
+attribute.  The result will be four copies of the `Tmp-Integer-0`
+attribute, which each carry one octet of the IP address.
+
+.Example Creating multiple Attributes
+----
+&Tmp-Integer-0 := %explode("192.168.0.1", '.')
+----
+
+If you need to cast an entire list to a value, then the value being
+cast should be surrounded by brackets.
+
+In the following example, the `%explode()` function will return a list
+of four values: `{"192", "168", "0", "1"}`.  Casting that to a string
+causes the values to be merged together, The resulting string is
+`"19216801"`.
+
+If you need to add text in between each list entry, see the
+`%concat()` function in the xref:xlat/builtin.adoc[built-in
+expansions] list.
+
+.Example Creating multiple Attributes
+----
+&reply.Reply-Message := (string) (%explode("192.168.0.1", '.'))
+----
+
 === Other Data Types
 
 Other data types such as `ethernet`, etc. can generally be cast
index 7edebc648ac545fd5281b68046630079dd2f1437..dd087b3a669a68539e5481b1d78ca8c7505d7183 100644 (file)
@@ -1,7 +1,6 @@
 #
 # PRE: if
 #
-
 # this fails, so the next edit is merged in and fails, too.
 &control -= &Password
 
        &User-Password = &request.User-Password
        &Tmp-String-0 = "ab c"
        &Tmp-String-0 = "de fg"
+#      &Tmp-Integer-0 = { 123, 456 }  # @todo - Doesn't work :(
        &Tmp-Integer-0 = 123
+       &Tmp-Integer-1 = 456
+       &Tmp-Integer-1 = 789
+}
+
+#
+#  &ref could return a list.
+#  (&ref) means "treat the list as one value"
+#  (string) (&ref) casts that one value to a string
+#    and returns one string
+#
+&Tmp-String-2 := %{(string) (&control.Tmp-Integer-1[*])}
+if !(&Tmp-String-2 == "456789") {
+       test_fail
 }
 
+#
+#  &ref could return a list.
+#  (&ref) means "treat the list as one value"
+#  (string) (&ref) casts each value to a string
+#    and returns a list of strings
+#
+&Tmp-String-2 := %{(string) &control.Tmp-Integer-1[*]}
+if !(&Tmp-String-2[0] == "456") {
+       test_fail
+}
+
+if !(&Tmp-String-2[1] == "789") {
+       test_fail
+}
+
+&control -= &Tmp-Integer-1[*]
 ok     # separate updates
 
+
 &control += {
        &Tmp-String-1 = %concat(%{control.[*]}, ', ')
 }