NAME
    Data::Transmute - Transmute (transform) data structure using rules data

VERSION
    This document describes version 0.040 of Data::Transmute (from Perl
    distribution Data-Transmute), released on 2024-07-17.

SYNOPSIS
     use Data::Transmute qw(
         transmute_data
         reverse_rules
     );

     my $transmuted_data = transmute_data(
         data => \@data,
         rules => [

             # CREATING HASH KEY

             # this rule only applies when data is a hash, when data is not a hash
             # this will do nothing. create a single new hash key, error if key
             # already exists.
             [create_hash_key => {name=>'foo', value=>1}],

             # create another hash key, but this time ignore/noop if key already
             # exists (ignore=1). this is like INSERT IGNORE in SQL. note: this
             # makes the rule irreversible.
             [create_hash_key => {name=>'bar', value=>2, ignore=>1}],

             # create yet another key, this time replace existing keys (replace=1).
             # this is like REPLACE INTO in SQL. note: this makes the rule
             # irreversible.
             [create_hash_key => {name=>'baz', value=>3, replace=>1}],

             # compute value with coderef (supply 'value_code' instead of 'value').
             # note: this makes the rule irreversible.
             [create_hash_key => {name=>'baz', value_code=>sub {uc($_[0]}, replace=>1}],

             # RENAMING HASH KEY

             # this rule only applies when data is a hash, when data is not a hash
             # this will do nothing. rename a single key, error if old name doesn't
             # exist or new name exists.
             [rename_hash_key => {from=>'qux', to=>'quux'}],

             # rename another key, but this time ignore if old name doesn't exist
             # (ignore=1) or if new name already exists (replace=1)
             [rename_hash_key => {from=>'corge', to=>'grault', ignore_missing_from=>1, replace=>1}],


             # MODIFYING HASH VALUE

             # this rule only applies when data is a hash, when data is not a hash
             # this will do nothing. change the value of a single keypair (key
             # specified in 'name'), error if key doesn't exist or old value doesn't
             # equal specified ('from').
             [modify_hash_value => {name=>'foo', from=>'old', to=>'new'}],

             # 'from' is optional, but if you omit it, the rule becomes
             # irreversible.
             [modify_hash_value => {name=>'foo', to=>'new'}],

             # instead of specifying new value in 'to', you can compute new value
             # using 'to_code'. note: if you do this the rule becomes irreversible.
             [modify_hash_value => {name=>'foo', from_old=>'old', to_code=>sub {uc $_[0]}}],


             # DELETING HASH KEY

             # this rule only applies when data is a hash, when data is not a hash
             # this will do nothing. delete a single key, will noop if key already
             # doesn't exist.
             [delete_hash_key => {name=>'garply'}],


             # APPLYING (SUB)RULES TO ARRAY ELEMENTS

             # this rule only applies when data is an arrayref, when data is not an
             # array this will do nothing. for each array element, apply transmute
             # rules to it.
             [transmute_array_elems => {
                  rules => [...],              # or 'rules_module'
              }],

             # you can select only certain elements to transmute by using one+ of:
             # index_is, index_in, index_match, index_filter.
             [transmute_array_elems => {
                  rules => [...],              # or 'rules_module'
                  #index_is => 1,              # only transmute 2nd element (index is 0-based)
                  #index_in => [0,1,2],        # only transmute the first 3 elements
                  #index_match => qr/.../,     # only transmute elements where the index matches a regex
                  #index_filter => sub{...},   # only transmute elements where $filter->(index=>$index) returns true
              }],


             # APPLYING (SUB)RULES TO HASH VALUES

             # this rule only applies when data is a hashref, when data is not a
             # hash this will do nothing. for each hash value, apply transmute rules
             # to it.
             [transmute_hash_values => {
                  rules => [...],            # or 'rules_module'

              }],

             # you can select only certain keys to transmute by using one+ of:
             # key_is, key_in, key_match, key_filter.
             [transmute_hash_values => {
                  rules => [...],            # or 'rules_module'
                  #key_is => 'foo',          # only transmute value of key 'foo'
                  #key_in => ['foo', 'bar'], # only transmute value of keys 'foo', 'bar'
                  #key_match => qr/.../,     # only transmute value of keys that match a regex
                  #key_filter => sub{...},   # only transmute value of keys where $filter->(key=>$key) returns true
              }],


             # APPLYING (SUB)RULES TO NODES

             # this rule will transmute data, then recurse (walk) to array elements
             # (if data is an array) and hash values (if data is a hash). can handle
             # circular references. this rule is irreversible.
             [transmute_nodes => {
                  rules => [...],              # or 'rules_module'
              }],

         ],
     );

    You can also load rules from a "Data::Transmute::Rules::*" module:

     transmute_data(
         data => $data,
         rules_module => 'Convert_Proj1_Data_To_Proj2', # will load Data::Transmute::Rules::Convert_Proj1_Data_To_Proj2 and read its @RULES package variable
     );

DESCRIPTION
    This module provides routines to transmute (transform) a data structure
    in-place using rules which is another data structure (an arrayref of
    rule specifications).

    One use-case for this module is to convert/upgrade configuration files.

RULES
    Rules is an array of rule specifications.

    Each rule specification: [$funcname, \%args]

    \%args: a special arg will be inserted: "data".

  create_hash_key
    This rule only applies when data is a hash, when data is not a hash this
    will do nothing. Create a single new hash key, error if key already
    exists.

    Known arguments ("*" means required):

    *   name*

    *   value

        Either "value" or "value_code" is required.

    *   value_code

        Either "value" or "value_code" is required.

        Instead of specifying value, you can also supply a coderef to
        compute the value. The coderef will be passed the current value of
        the hash key (or undef if there is none).

        If you supply "value_code", your rule will become irreversible.

    *   ignore

        Bool. If set to true, will ignore/noop if key already exists. This
        is like INSERT IGNORE (INSERT OR IGNORE) in SQL.

        If you set "ignore" to true, your rule will become irreversible.

    *   replace

        Bool. If set to true, will replace existing keys. This is like
        REPLACE INTO in SQL.

        If you set "replace" to true, your rule will become irreversible.

    *   transmute_object

        Bool, default true. By default, blessed hash is also transmuted. But
        if you set this to false, blessed hash will not be touched.

  rename_hash_key
    This rule only applies when data is a hash, when data is not a hash this
    will do nothing. Rename a single key, error if old name doesn't exist or
    new name exists.

    Known arguments ("*" means required):

    *   from*

    *   to*

    *   ignore_missing_from

        Bool. If set to true, will noop (instead of error) if old name
        doesn't exist.

    *   replace

        Bool. If set to true, will overwrite (instead of error) when target
        key already exists.

    *   transmute_object

        Bool, default true. By default, blessed hash is also transmuted. But
        if you set this to false, blessed hash will not be touched.

  modify_hash_value
    This rule only applies when data is a hash, when data is not a hash this
    will do nothing. Modify a single hash value from original value *from*
    to new value *to*. Key must exist, and value must originally be *from*.

    Known arguments ("*" means required):

    *   name*

        String. Key name.

    *   from*

        String or undef. Original value.

    *   to

        String or undef. New value.

        Either "to" or "to_code" is required.

    *   to_code

        Coderef. Instead of specifying new vlaue via "to", you can also
        supply "to_code" (a coderef) to compute the value. The coderef will
        be passed the current value.

        If you set "to_code", your rule will become irreversible.

    *   transmute_object

        Bool, default true. By default, blessed hash is also transmuted. But
        if you set this to false, blessed hash will not be touched.

  delete_hash_key
    This rule only applies when data is a hash, when data is not a hash this
    will do nothing. Delete a single key, will noop if key already doesn't
    exist.

    Known arguments ("*" means required):

    *   name*

    *   transmute_object

        Bool, default true. By default, blessed hash is also transmuted. But
        if you set this to false, blessed hash will not be touched.

  transmute_array_elems
    This rule only applies when data is an arrayref, when data is not an
    array this will do nothing. for each array element, apply transmute
    rules to it.

    Known arguments ("*" means required):

    *   rules

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

    *   rules_module

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

    *   index_is

    *   index_in

    *   index_match

    *   index_filter

        Coderef. Only transmute elements where $coderef->(index=>$index) is
        true. Aside from "index", the coderef will also receive these
        arguments: "rules" (the rule), "array" (the array).

    *   transmute_object

        Bool, default true. By default, blessed array is also transmuted.
        But if you set this to false, blessed array will not be touched.

  transmute_hash_values
    This rule only applies when data is a hashref, when data is not a hash
    this will do nothing. For each hash value, apply transmute rules to it.

    Known arguments ("*" means required):

    *   rules

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

    *   rules_module

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

    *   key_is

    *   key_in

    *   key_match

    *   key_filter

        Coderef. Only transmute value of keys where $coderef->(key=>$key) is
        true. Aside from "key", the coderef will also receive these
        arguments: "rules" (the rule), "hash" (the hash).

    *   transmute_object

        Bool, default true. By default, blessed hash is also transmuted. But
        if you set this to false, blessed hash will not be touched.

  transmute_nodes
    This rule will transmute data, then recurse (walk) to array elements (if
    data is an array) or hash values (if data is a hash) and transmute each
    of those child data. Can handle circular references.

    This rule is not irreversible.

    Known arguments ("*" means required):

    *   recurse_object

        Boolean, default false. Whether to recurse into (hash-based and
        array-based) objects. By default, objects are not recursed. Set this
        to true to recurse into objects.

    *   rules

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

    *   rules_module

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

FUNCTIONS
  transmute_data
    Usage:

     $data = transmute_data(%args)

    Transmute data structure, die on failure. Input data is specified in the
    "data" argument, which will be modified in-place (so you'll need to
    clone it first if you don't want to modify the original data). Rules is
    specified in "rules" argument.

    Known arguments ("*" means required):

    *   data*

    *   rules

        Array of rules. See "RULES" for more details.

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

    *   rules_module

        Specify name of module (without the "Data::Transmute::Rules::"
        prefix) which contains the actual rules. The module will be loaded
        and the rules retrieved from its @RULES package variable.

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

  reverse_rules
    Usage:

     my $reverse_rules = reverse_rules(rules => [...]);

    Create a reverse of rules, die on failure. For example, this set of
    rules:

     [
       [create_hash_key => {name=>'a', value=>1}],
       [rename_hash_key => {from=>'c', to=>'d'}],
     ]

    when reversed will become:

     [
       [rename_hash_key => {from=>'d', to=>'c'}],
       [delete_hash_key => {name=>'a'}],
     ]

    Some rules cannot be reversed, e.g. "delete_hash_key" so when given
    rules that contain that, the function will die. A rule can only be
    reversed for a subset of arguments, e.g. "rename_hash_key" with "ignore"
    set to true or "replace" set to true cannot be reversed.

    The reverse of a set of rules can be used to reverse back a transmuted
    data back to the original.

    Known arguments ("*" means required):

    *   rules

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

        See "transmute_data" for more details.

    *   rules_module

        Either "rules" or "rules_module" is required. "rules" takes
        precedence over "rules_module".

        See "transmute_data" for more details.

ENVIRONMENT
  LOG_DATA_TRANSMUTE_STEP
    Boolean. If set to true, will log each transmute step (rule by rule) at
    the trace level using Log::ger.

TODOS
    Function to mass rename keys (by regex substitution, prefix, custom Perl
    code, ...). But this cannot produce reverse of rule.

    Function to mass delete keys (by regex, prefix, ...). But this cannot
    produce reverse of rule.

HOMEPAGE
    Please visit the project's homepage at
    <https://metacpan.org/release/Data-Transmute>.

SOURCE
    Source repository is at
    <https://github.com/perlancar/perl-Data-Transmute>.

SEE ALSO
    Hash::Transform is similar in concept. It allows transforming a hash
    using rules encoded in a hash. However, the rules only allow for simpler
    transformations: rename a key, create a key with a specified value,
    create a key that from a string-based join of other keys/strings. For
    more complex needs, you'll have to supply a coderef to do the
    transformation yourself manually. Another thing I find limiting is that
    the rules is a hash, which means there is no way to specify order of
    processing. And of course, you cannot transform non-hash data.

    Config::Model, which you can also use to convert/upgrade configuration
    files. But I find this module slightly too heavyweight for the simpler
    needs that I have, hence I created Data::Transmute.

    Bencher::Scenarios::DataTransmute

AUTHOR
    perlancar <perlancar@cpan.org>

CONTRIBUTOR
    Steven Haryanto <stevenharyanto@gmail.com>

CONTRIBUTING
    To contribute, you can send patches by email/via RT, or send pull
    requests on GitHub.

    Most of the time, you don't need to build the distribution yourself. You
    can simply modify the code, then test via:

     % prove -l

    If you want to build the distribution (e.g. to try to install it locally
    on your system), you can install Dist::Zilla,
    Dist::Zilla::PluginBundle::Author::PERLANCAR,
    Pod::Weaver::PluginBundle::Author::PERLANCAR, and sometimes one or two
    other Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps
    required beyond that are considered a bug and can be reported to me.

COPYRIGHT AND LICENSE
    This software is copyright (c) 2024, 2020, 2019, 2015 by perlancar
    <perlancar@cpan.org>.

    This is free software; you can redistribute it and/or modify it under
    the same terms as the Perl 5 programming language system itself.

BUGS
    Please report any bugs or feature requests on the bugtracker website
    <https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Transmute>

    When submitting a bug or request, please include a test-file or a patch
    to an existing test-file that illustrates the bug or desired feature.