diff --git a/docs/predicate/base64_encoded_3.md b/docs/predicate/base64_encoded_3.md new file mode 100644 index 00000000..f2d2f8ae --- /dev/null +++ b/docs/predicate/base64_encoded_3.md @@ -0,0 +1,372 @@ +--- +sidebar_position: 9 +--- +[//]: # (This file is auto-generated. Please do not modify it yourself.) + +# base64_encoded/3 + +## Description + +`base64_encoded/3` is a predicate that unifies a string to a base64 encoded string as specified by [RFC 4648](). + +The signature is as follows: + +```text +base64_encoded(+Plain, -Encoded, +Options) is det +base64_encoded(-Plain, +Encoded, +Options) is det +base64_encoded(+Plain, +Encoded, +Options) is det +``` + +Where: + +- Plain is an atom, a list of character codes, or list of characters containing the unencoded \(plain\) text. +- Encoded is an atom or string containing the base64 encoded text. +- Options is a list of options that can be used to control the encoding process. + +## Options + +The following options are supported: + +- padding\(\+Boolean\) + +If true \(default\), the output is padded with = characters. + +- charset\(\+Charset\) + +Define the encoding character set to use. The \(default\) 'classic' uses the classical rfc2045 characters. The value 'url' uses URL and file name friendly characters. + +- as\(\+Type\) + +Defines the type of the output. One of string \(default\) or atom. + +- encoding\(\+Encoding\) + +Encoding to use for translation between \(Unicode\) text and bytes \(Base64 is an encoding for bytes\). Default is utf8. + +## Examples + +### Encode a string into a Base64 encoded string (with default options) + +This scenario demonstrates how to encode a plain string into its Base64 representation using the `base64_encoded/3` +predicate. The default options are used, meaning: + +- The output is returned as a list of characters (`as(string)`). +- Padding characters (`=`) are included (`padding(true)`). +- The classic Base64 character set is used (`charset(classic)`), not the URL-safe variant. + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded('Hello World', X, []). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "['S','G','V',s,b,'G','8',g,'V','2','9',y,b,'G','Q',=]" +``` + +### Encode a string into a Base64 encoded atom + +This scenario demonstrates how to encode a plain string into a Base64-encoded atom using the `base64_encoded/3` +predicate. The `as(atom)` option is specified, so the result is returned as a Prolog atom instead of a character +list. All other options use their default values. + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded('Hello World', X, [as(atom)]). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "'SGVsbG8gV29ybGQ='" +``` + +### Encode a string into a Base64 encoded atom without padding + +This scenario demonstrates how to encode a plain string into a Base64-encoded atom using the `base64_encoded/3` predicate +with custom options. The following options are used: + +- `as(atom)` – the result is returned as a Prolog atom. +- `padding(false)` – padding characters (`=`) are omitted. +- The classic Base64 character set is used by default (`charset(classic)`). + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded('Hello World', X, [as(atom), padding(false)]). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "'SGVsbG8gV29ybGQ'" +``` + +### Encode a String into a Base64 encoded atom in URL-Safe mode + +This scenario demonstrates how to encode a plain string into a Base64-encoded atom using the `base64_encoded/3` predicate +with URL-safe encoding. The following options are used: + +- `as(atom)` – the result is returned as a Prolog atom. +- `charset(url)` – the URL-safe Base64 alphabet is used (e.g., `-` and `_` instead of `+` and `/`). +- Padding characters are included by default (`padding(true)`). + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded('<>', Classic, [as(atom), charset(classic)]), +base64_encoded('<>', UrlSafe, [as(atom), charset(url)]). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3976 +answer: + has_more: false + variables: ["Classic", "UrlSafe"] + results: + - substitutions: + - variable: Classic + expression: "'PDw/Pz8+Pg=='" + - variable: UrlSafe + expression: "'PDw_Pz8-Pg=='" +``` + +### Decode a Base64 encoded String into plain text + +This scenario demonstrates how to decode a Base64-encoded value back into plain text using the `base64_encoded/3` predicate. +The encoded input can be provided as a character list or an atom. In this example, default options are used: +• The result (plain text) is returned as a character list (`as(string)`). +• Padding characters in the input are allowed (`padding(true)`). +• The classic Base64 character set is used (`charset(classic)`). + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded(X, 'SGVsbG8gV29ybGQ=', []). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "['H',e,l,l,o,' ','W',o,r,l,d]" +``` + +### Decode a Base64 Encoded string into a plain atom + +This scenario demonstrates how to decode a Base64-encoded value back into plain text using the `base64_encoded/3` predicate, +with the result returned as a Prolog atom. The following options are used: + +- `as(atom)` – the decoded plain text is returned as an atom. +- `padding(true)` – padding characters in the input are allowed (default). +- `charset(classic)` – the classic Base64 character set is used (default). + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded(X, 'SGVsbG8gV29ybGQ=', [as(atom)]). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "'Hello World'" +``` + +### Error on incorrect charset option + +This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid value is provided for the +`charset` option. + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded('Hello World', X, [charset(bad)]). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - error: "error(domain_error(charset,bad),base64_encoded/3)" + substitutions: +``` + +### Error on incorrect padding option + +This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid value is provided for the +`padding` option. + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded('Hello World', X, [padding(bad)]). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - error: "error(domain_error(padding,bad),base64_encoded/3)" + substitutions: +``` + +### Error on incorrect as option + +This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid value is provided for the +`as` option. + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded('Hello World', X, [as(bad)]). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - error: "error(domain_error(as,bad),base64_encoded/3)" + substitutions: +``` + +### Error on incorrect encoding option + +This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid value is provided for the +`encoding` option. + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded(X, 'SGVsbG8gV29ybGQ=', [as(atom), encoding(unknown)]). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - error: "error(type_error(charset,unknown),base64_encoded/3)" + substitutions: +``` + +### Error on incorrect encoding option (2) + +This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid type is provided for the +`encoding` option. + +Here are the steps of the scenario: + +- **Given** the query: + +``` prolog +base64_encoded(X, 'SGVsbG8gV29ybGQ=', [encoding(bad, 'very bad')]). +``` + +- **When** the query is run +- **Then** the answer we get is: + +``` yaml +height: 42 +gas_used: 3975 +answer: + has_more: false + variables: ["X"] + results: + - error: "error(type_error(option,encoding(bad,very bad)),base64_encoded/3)" + substitutions: +``` diff --git a/docs/predicate/bech32_address_2.md b/docs/predicate/bech32_address_2.md index 5e462f65..fb6e0d4e 100644 --- a/docs/predicate/bech32_address_2.md +++ b/docs/predicate/bech32_address_2.md @@ -1,5 +1,5 @@ --- -sidebar_position: 9 +sidebar_position: 10 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/block_header_1.md b/docs/predicate/block_header_1.md index d2a32b92..b2e0bf83 100644 --- a/docs/predicate/block_header_1.md +++ b/docs/predicate/block_header_1.md @@ -1,5 +1,5 @@ --- -sidebar_position: 10 +sidebar_position: 11 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/block_height_1.md b/docs/predicate/block_height_1.md index 7a480034..11d5a0e9 100644 --- a/docs/predicate/block_height_1.md +++ b/docs/predicate/block_height_1.md @@ -1,5 +1,5 @@ --- -sidebar_position: 11 +sidebar_position: 12 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/block_time_1.md b/docs/predicate/block_time_1.md index 8b81831c..4579b4bf 100644 --- a/docs/predicate/block_time_1.md +++ b/docs/predicate/block_time_1.md @@ -1,5 +1,5 @@ --- -sidebar_position: 12 +sidebar_position: 13 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/chain_id_1.md b/docs/predicate/chain_id_1.md index 46b66a05..8748853d 100644 --- a/docs/predicate/chain_id_1.md +++ b/docs/predicate/chain_id_1.md @@ -1,5 +1,5 @@ --- -sidebar_position: 13 +sidebar_position: 14 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/consult_1.md b/docs/predicate/consult_1.md index 14f342c6..3c4bb446 100644 --- a/docs/predicate/consult_1.md +++ b/docs/predicate/consult_1.md @@ -1,5 +1,5 @@ --- -sidebar_position: 14 +sidebar_position: 15 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/crypto_data_hash_3.md b/docs/predicate/crypto_data_hash_3.md index 645a3b81..a9f89276 100644 --- a/docs/predicate/crypto_data_hash_3.md +++ b/docs/predicate/crypto_data_hash_3.md @@ -1,5 +1,5 @@ --- -sidebar_position: 15 +sidebar_position: 16 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/current_output_1.md b/docs/predicate/current_output_1.md index b9d3a38c..587540c0 100644 --- a/docs/predicate/current_output_1.md +++ b/docs/predicate/current_output_1.md @@ -1,5 +1,5 @@ --- -sidebar_position: 16 +sidebar_position: 17 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/did_components_2.md b/docs/predicate/did_components_2.md index 8a572afa..9918dac8 100644 --- a/docs/predicate/did_components_2.md +++ b/docs/predicate/did_components_2.md @@ -1,5 +1,5 @@ --- -sidebar_position: 17 +sidebar_position: 18 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/ecdsa_verify_4.md b/docs/predicate/ecdsa_verify_4.md index 39bd6270..c2c3fc2e 100644 --- a/docs/predicate/ecdsa_verify_4.md +++ b/docs/predicate/ecdsa_verify_4.md @@ -1,5 +1,5 @@ --- -sidebar_position: 18 +sidebar_position: 19 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/eddsa_verify_4.md b/docs/predicate/eddsa_verify_4.md index 6c668201..62f6d745 100644 --- a/docs/predicate/eddsa_verify_4.md +++ b/docs/predicate/eddsa_verify_4.md @@ -1,5 +1,5 @@ --- -sidebar_position: 19 +sidebar_position: 20 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/hex_bytes_2.md b/docs/predicate/hex_bytes_2.md index 7762bf5a..d88e4956 100644 --- a/docs/predicate/hex_bytes_2.md +++ b/docs/predicate/hex_bytes_2.md @@ -1,5 +1,5 @@ --- -sidebar_position: 20 +sidebar_position: 21 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/json_prolog_2.md b/docs/predicate/json_prolog_2.md index 27b5d5f4..5502d652 100644 --- a/docs/predicate/json_prolog_2.md +++ b/docs/predicate/json_prolog_2.md @@ -1,5 +1,5 @@ --- -sidebar_position: 21 +sidebar_position: 22 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/json_read_2.md b/docs/predicate/json_read_2.md index 3fdf10ee..9d1d741b 100644 --- a/docs/predicate/json_read_2.md +++ b/docs/predicate/json_read_2.md @@ -1,5 +1,5 @@ --- -sidebar_position: 22 +sidebar_position: 23 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/json_write_2.md b/docs/predicate/json_write_2.md index b893e0fc..9413bc06 100644 --- a/docs/predicate/json_write_2.md +++ b/docs/predicate/json_write_2.md @@ -1,5 +1,5 @@ --- -sidebar_position: 23 +sidebar_position: 24 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/open_3.md b/docs/predicate/open_3.md index 778a39b3..6b2922cf 100644 --- a/docs/predicate/open_3.md +++ b/docs/predicate/open_3.md @@ -1,5 +1,5 @@ --- -sidebar_position: 25 +sidebar_position: 26 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/open_4.md b/docs/predicate/open_4.md index dbefa51a..f96f4187 100644 --- a/docs/predicate/open_4.md +++ b/docs/predicate/open_4.md @@ -1,5 +1,5 @@ --- -sidebar_position: 24 +sidebar_position: 25 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/read_string_3.md b/docs/predicate/read_string_3.md index 4499960f..b00bd1e4 100644 --- a/docs/predicate/read_string_3.md +++ b/docs/predicate/read_string_3.md @@ -1,5 +1,5 @@ --- -sidebar_position: 26 +sidebar_position: 27 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/read_term_3.md b/docs/predicate/read_term_3.md index ca53a8be..f7f20128 100644 --- a/docs/predicate/read_term_3.md +++ b/docs/predicate/read_term_3.md @@ -1,5 +1,5 @@ --- -sidebar_position: 27 +sidebar_position: 28 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/retract_1.md b/docs/predicate/retract_1.md index e78cf6b8..5f88131d 100644 --- a/docs/predicate/retract_1.md +++ b/docs/predicate/retract_1.md @@ -1,5 +1,5 @@ --- -sidebar_position: 28 +sidebar_position: 29 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/source_file_1.md b/docs/predicate/source_file_1.md index 49460027..0542bab0 100644 --- a/docs/predicate/source_file_1.md +++ b/docs/predicate/source_file_1.md @@ -1,5 +1,5 @@ --- -sidebar_position: 29 +sidebar_position: 30 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/string_bytes_3.md b/docs/predicate/string_bytes_3.md index 3370c600..a3425d68 100644 --- a/docs/predicate/string_bytes_3.md +++ b/docs/predicate/string_bytes_3.md @@ -1,5 +1,5 @@ --- -sidebar_position: 30 +sidebar_position: 31 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/term_to_atom_2.md b/docs/predicate/term_to_atom_2.md index ca76db69..a8749b01 100644 --- a/docs/predicate/term_to_atom_2.md +++ b/docs/predicate/term_to_atom_2.md @@ -1,5 +1,5 @@ --- -sidebar_position: 31 +sidebar_position: 32 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/uri_encoded_3.md b/docs/predicate/uri_encoded_3.md index 4f82d9d6..006dced1 100644 --- a/docs/predicate/uri_encoded_3.md +++ b/docs/predicate/uri_encoded_3.md @@ -1,5 +1,5 @@ --- -sidebar_position: 32 +sidebar_position: 33 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/docs/predicate/write_term_3.md b/docs/predicate/write_term_3.md index dbfcc3a9..7f16ae9d 100644 --- a/docs/predicate/write_term_3.md +++ b/docs/predicate/write_term_3.md @@ -1,5 +1,5 @@ --- -sidebar_position: 33 +sidebar_position: 34 --- [//]: # (This file is auto-generated. Please do not modify it yourself.) diff --git a/x/logic/interpreter/registry.go b/x/logic/interpreter/registry.go index 3911e618..d9c3fa73 100644 --- a/x/logic/interpreter/registry.go +++ b/x/logic/interpreter/registry.go @@ -125,6 +125,7 @@ var registry = orderedmap.New[string, any]( {Key: "atomic_list_concat/3", Value: predicate.AtomicListConcat3}, {Key: "json_read/2", Value: predicate.JSONRead}, {Key: "json_write/2", Value: predicate.JSONWrite}, + {Key: "base64_encoded/3", Value: predicate.Base64Encoded}, }...), ) diff --git a/x/logic/predicate/encoding.go b/x/logic/predicate/encoding.go index 65401464..78ed476c 100644 --- a/x/logic/predicate/encoding.go +++ b/x/logic/predicate/encoding.go @@ -1,6 +1,7 @@ package predicate import ( + "encoding/base64" "encoding/hex" "github.com/axone-protocol/prolog/v2/engine" @@ -8,6 +9,143 @@ import ( "github.com/axone-protocol/axoned/v11/x/logic/prolog" ) +var ( + atomClassic = engine.NewAtom("classic") + atomURL = engine.NewAtom("url") + atomString = engine.NewAtom("string") + atomAtom = engine.NewAtom("atom") +) + +// Base64Encoded is a predicate that unifies a string to a base64 encoded string as specified by [RFC 4648]. +// +// The signature is as follows: +// +// base64_encoded(+Plain, -Encoded, +Options) is det +// base64_encoded(-Plain, +Encoded, +Options) is det +// base64_encoded(+Plain, +Encoded, +Options) is det +// +// Where: +// - Plain is an atom, a list of character codes, or list of characters containing the unencoded (plain) text. +// - Encoded is an atom or string containing the base64 encoded text. +// - Options is a list of options that can be used to control the encoding process. +// +// # Options +// +// The following options are supported: +// +// - padding(+Boolean) +// +// If true (default), the output is padded with = characters. +// +// - charset(+Charset) +// +// Define the encoding character set to use. The (default) 'classic' uses the classical rfc2045 characters. The value 'url' +// uses URL and file name friendly characters. +// +// - as(+Type) +// +// Defines the type of the output. One of string (default) or atom. +// +// - encoding(+Encoding) +// +// Encoding to use for translation between (Unicode) text and bytes (Base64 is an encoding for bytes). Default is utf8. +// +// [RFC 4648]: https://rfc-editor.org/rfc/rfc4648.html +func Base64Encoded(_ *engine.VM, plain, encoded, options engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise { + encoding, err := getBase64Encoding(options, env) + if err != nil { + return engine.Error(err) + } + + asOpt, err := prolog.GetOptionWithDefault(prolog.AtomAs, options, atomString, env) + if err != nil { + return engine.Error(err) + } + + forwardConverter := func(value []engine.Term, _ engine.Term, env *engine.Env) ([]engine.Term, error) { + src, err := prolog.TextTermToString(value[0], env) + if err != nil { + return nil, err + } + dst := encoding.EncodeToString([]byte(src)) + + switch asOpt { + case atomString: + return []engine.Term{prolog.StringToCharacterListTerm(dst)}, nil + case atomAtom: + return []engine.Term{engine.NewAtom(dst)}, nil + default: + return nil, engine.DomainError(prolog.AtomAs, asOpt, env) + } + } + backwardConverter := func(value []engine.Term, options engine.Term, env *engine.Env) ([]engine.Term, error) { + src, err := prolog.TextTermToString(value[0], env) + if err != nil { + return nil, err + } + + dst, err := encoding.DecodeString(src) + if err != nil { + return nil, + prolog.WithError( + engine.DomainError(prolog.ValidEncoding("base64"), value[0], env), err, env) + } + + encodingOpt, err := prolog.GetOptionAsAtomWithDefault(prolog.AtomEncoding, options, prolog.AtomUtf8, env) + if err != nil { + return nil, err + } + dstStr, err := prolog.Decode(value[0], dst, encodingOpt, env) + if err != nil { + return nil, err + } + + switch asOpt { + case atomString: + return []engine.Term{prolog.StringToCharacterListTerm(dstStr)}, nil + case atomAtom: + return []engine.Term{engine.NewAtom(dstStr)}, nil + default: + return nil, engine.DomainError(prolog.AtomAs, asOpt, env) + } + } + + return prolog.UnifyFunctionalPredicate( + []engine.Term{plain}, []engine.Term{encoded}, options, forwardConverter, backwardConverter, cont, env) +} + +// getBase64Encoding returns the base64 encoding based on the options provided. +func getBase64Encoding(options engine.Term, env *engine.Env) (*base64.Encoding, error) { + var encoding *base64.Encoding + + charsetOpt, err := prolog.GetOptionAsAtomWithDefault(prolog.AtomCharset, options, atomClassic, env) + if err != nil { + return nil, err + } + switch charsetOpt { + case atomClassic: + encoding = base64.StdEncoding + case atomURL: + encoding = base64.URLEncoding + default: + return nil, engine.DomainError(prolog.AtomCharset, charsetOpt, env) + } + + paddingOpt, err := prolog.GetOptionWithDefault(prolog.AtomPadding, options, prolog.AtomTrue, env) + if err != nil { + return nil, err + } + switch paddingOpt { + case prolog.AtomTrue: + encoding = encoding.WithPadding(base64.StdPadding) + case prolog.AtomFalse: + encoding = encoding.WithPadding(base64.NoPadding) + default: + return nil, engine.DomainError(prolog.AtomPadding, paddingOpt, env) + } + return encoding, nil +} + // HexBytes is a predicate that unifies hexadecimal encoded bytes to a list of bytes. // // The signature is as follows: diff --git a/x/logic/predicate/features/base64_encoded_3.feature b/x/logic/predicate/features/base64_encoded_3.feature new file mode 100644 index 00000000..16734dc0 --- /dev/null +++ b/x/logic/predicate/features/base64_encoded_3.feature @@ -0,0 +1,392 @@ +Feature: base64_encoded/3 + This feature is to test the base64_encoded/3 predicate. + + @great_for_documentation + Scenario: Encode a string into a Base64 encoded string (with default options) + This scenario demonstrates how to encode a plain string into its Base64 representation using the `base64_encoded/3` + predicate. The default options are used, meaning: + - The output is returned as a list of characters (`as(string)`). + - Padding characters (`=`) are included (`padding(true)`). + - The classic Base64 character set is used (`charset(classic)`), not the URL-safe variant. + + Given the query: + """ prolog + base64_encoded('Hello World', X, []). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "['S','G','V',s,b,'G','8',g,'V','2','9',y,b,'G','Q',=]" + """ + + @great_for_documentation + Scenario: Encode a string into a Base64 encoded atom + This scenario demonstrates how to encode a plain string into a Base64-encoded atom using the `base64_encoded/3` + predicate. The `as(atom)` option is specified, so the result is returned as a Prolog atom instead of a character + list. All other options use their default values. + + Given the query: + """ prolog + base64_encoded('Hello World', X, [as(atom)]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "'SGVsbG8gV29ybGQ='" + """ + + @great_for_documentation + Scenario: Encode a string into a Base64 encoded atom without padding + This scenario demonstrates how to encode a plain string into a Base64-encoded atom using the `base64_encoded/3` predicate + with custom options. The following options are used: + - `as(atom)` – the result is returned as a Prolog atom. + - `padding(false)` – padding characters (`=`) are omitted. + - The classic Base64 character set is used by default (`charset(classic)`). + + Given the query: + """ prolog + base64_encoded('Hello World', X, [as(atom), padding(false)]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "'SGVsbG8gV29ybGQ'" + """ + + @great_for_documentation + Scenario: Encode a String into a Base64 encoded atom in URL-Safe mode + This scenario demonstrates how to encode a plain string into a Base64-encoded atom using the `base64_encoded/3` predicate + with URL-safe encoding. The following options are used: + - `as(atom)` – the result is returned as a Prolog atom. + - `charset(url)` – the URL-safe Base64 alphabet is used (e.g., `-` and `_` instead of `+` and `/`). + - Padding characters are included by default (`padding(true)`). + + Given the query: + """ prolog + base64_encoded('<>', Classic, [as(atom), charset(classic)]), + base64_encoded('<>', UrlSafe, [as(atom), charset(url)]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3976 + answer: + has_more: false + variables: ["Classic", "UrlSafe"] + results: + - substitutions: + - variable: Classic + expression: "'PDw/Pz8+Pg=='" + - variable: UrlSafe + expression: "'PDw_Pz8-Pg=='" + """ + + @great_for_documentation + Scenario: Decode a Base64 encoded String into plain text + This scenario demonstrates how to decode a Base64-encoded value back into plain text using the `base64_encoded/3` predicate. + The encoded input can be provided as a character list or an atom. In this example, default options are used: + • The result (plain text) is returned as a character list (`as(string)`). + • Padding characters in the input are allowed (`padding(true)`). + • The classic Base64 character set is used (`charset(classic)`). + + Given the query: + """ prolog + base64_encoded(X, 'SGVsbG8gV29ybGQ=', []). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "['H',e,l,l,o,' ','W',o,r,l,d]" + """ + + @great_for_documentation + Scenario: Decode a Base64 Encoded string into a plain atom + This scenario demonstrates how to decode a Base64-encoded value back into plain text using the `base64_encoded/3` predicate, + with the result returned as a Prolog atom. The following options are used: + - `as(atom)` – the decoded plain text is returned as an atom. + - `padding(true)` – padding characters in the input are allowed (default). + - `charset(classic)` – the classic Base64 character set is used (default). + + Given the query: + """ prolog + base64_encoded(X, 'SGVsbG8gV29ybGQ=', [as(atom)]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - substitutions: + - variable: X + expression: "'Hello World'" + """ + + @great_for_documentation + Scenario: Error on incorrect charset option + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid value is provided for the + `charset` option. + + Given the query: + """ prolog + base64_encoded('Hello World', X, [charset(bad)]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(domain_error(charset,bad),base64_encoded/3)" + substitutions: + """ + + Scenario: Error on incorrect charset option (2) + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid type is provided for the + `charset` option. + + Given the query: + """ prolog + base64_encoded('Hello World', X, [charset("bad")]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(type_error(atom,[b,a,d]),base64_encoded/3)" + substitutions: + """ + + @great_for_documentation + Scenario: Error on incorrect padding option + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid value is provided for the + `padding` option. + + Given the query: + """ prolog + base64_encoded('Hello World', X, [padding(bad)]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(domain_error(padding,bad),base64_encoded/3)" + substitutions: + """ + + Scenario: Error on incorrect padding option (2) + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid type is provided for the + `padding` option. + + Given the query: + """ prolog + base64_encoded('Hello World', X, [padding(bad, 'very bad')]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(type_error(option,padding(bad,very bad)),base64_encoded/3)" + substitutions: + """ + + @great_for_documentation + Scenario: Error on incorrect as option + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid value is provided for the + `as` option. + + Given the query: + """ prolog + base64_encoded('Hello World', X, [as(bad)]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(domain_error(as,bad),base64_encoded/3)" + substitutions: + """ + + Scenario: Error on incorrect as option (2) + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid type is provided for the + `as` option. + + Given the query: + """ prolog + base64_encoded('Hello World', X, [as(bad, 'very bad')]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(type_error(option,as(bad,very bad)),base64_encoded/3)" + substitutions: + """ + + Scenario: Error on incorrect plain type input + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid plain type input is provided. + + Given the query: + """ prolog + base64_encoded(wrong(input), X, []). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(type_error(text,wrong(input)),base64_encoded/3)" + substitutions: + """ + + Scenario: Error on incorrect Base64 encoded input + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid encoded input is provided. + + Given the query: + """ prolog + base64_encoded(X, '!!!!', [as(atom)]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(domain_error(encoding(base64),!!!!),[i,l,l,e,g,a,l, ,b,a,s,e,6,4, ,d,a,t,a, ,a,t, ,i,n,p,u,t, ,b,y,t,e, ,0],base64_encoded/3)" + substitutions: + """ + + Scenario: Error on incorrect encoded type input + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid encoded type input is provided. + + Given the query: + """ prolog + base64_encoded(X, wrong(input), []). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(type_error(text,wrong(input)),base64_encoded/3)" + substitutions: + """ + + @great_for_documentation + Scenario: Error on incorrect encoding option + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid value is provided for the + `encoding` option. + + Given the query: + """ prolog + base64_encoded(X, 'SGVsbG8gV29ybGQ=', [as(atom), encoding(unknown)]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(type_error(charset,unknown),base64_encoded/3)" + substitutions: + """ + + @great_for_documentation + Scenario: Error on incorrect encoding option (2) + This scenario demonstrates how the `base64_encoded/3` predicate behaves when an invalid type is provided for the + `encoding` option. + + Given the query: + """ prolog + base64_encoded(X, 'SGVsbG8gV29ybGQ=', [encoding(bad, 'very bad')]). + """ + When the query is run + Then the answer we get is: + """ yaml + height: 42 + gas_used: 3975 + answer: + has_more: false + variables: ["X"] + results: + - error: "error(type_error(option,encoding(bad,very bad)),base64_encoded/3)" + substitutions: + """ diff --git a/x/logic/prolog/atom.go b/x/logic/prolog/atom.go index 220e2085..308e1f26 100644 --- a/x/logic/prolog/atom.go +++ b/x/logic/prolog/atom.go @@ -33,6 +33,8 @@ var ( AtomOctet = engine.NewAtom("octet") // AtomPadding is the term used to indicate the padding encoding type option. AtomPadding = engine.NewAtom("padding") + // AtomCharset is the term used to indicate the charset encoding type option. + AtomCharset = engine.NewAtom("charset") // AtomPair are terms with principal functor (-)/2. // For example, the term -(A, B) denotes the pair of elements A and B. AtomPair = engine.NewAtom("-") @@ -47,6 +49,8 @@ var ( AtomSegment = engine.NewAtom("segment") // AtomText is the term used to indicate the atom text. AtomText = engine.NewAtom("text") + // AtomBoolean is the term used to indicate the atom boolean. + AtomBoolean = engine.NewAtom("boolean") // AtomTrue is the term true. AtomTrue = engine.NewAtom("true") // AtomUtf8 is the term used to indicate the UTF-8 encoding type option. diff --git a/x/logic/prolog/error.go b/x/logic/prolog/error.go index 237dd7fa..5e16c7fd 100644 --- a/x/logic/prolog/error.go +++ b/x/logic/prolog/error.go @@ -51,6 +51,8 @@ var ( AtomTypeJSON = engine.NewAtom("json") // AtomTypeURIComponent is the term used to represent the URI component type. AtomTypeURIComponent = engine.NewAtom("uri_component") + // AtomTypeBoolean is the term used to represent the boolean type. + AtomTypeBoolean = engine.NewAtom("boolean") ) var (