NAME
    Text::Fragment - Manipulate fragments in text

VERSION
    This document describes version 0.111 of Text::Fragment (from Perl
    distribution Text-Fragment), released on 2025-10-15.

SYNOPSIS
     use Text::Fragment qw(list_fragments
                           get_fragment
                           set_fragment_attrs
                           insert_fragment
                           delete_fragment);

     my $text = <<_;
     foo = "some value"
     baz = 0
     _

    To insert a fragment:

     my $res = insert_fragment(text=>$text, id=>'bar', payload=>'bar = 2');

    "$res->[2]{text}" will now contain:

     foo = "some value"
     baz = 0
     bar = 2 # FRAGMENT id=bar

    To replace a fragment:

     $res = insert_fragment(text=>$res->[2], id='bar', payload=>'bar = 3');

    "$res->[2]{text}" will now contain:

     foo = "some value"
     baz = 0
     bar = 3 # FRAGMENT id=bar

    and "$res->[2]{orig_payload}" will contain the payload before being
    replaced:

     bar = 2

    To delete a fragment:

     $res = delete_fragment(text=>$res->[2], id=>'bar');

    To list fragments contained in a text:

     $res = list_fragments(text=>$text);

    To get a fragment:

     $res = get_fragment(text=>$text, id=>'bar');

    To set fragment attributes:

     $res = set_fragment_attrs(text=>$text, id=>'bar', attrs=>{name=>'val', ...});

DESCRIPTION
    A fragment is a single line or a group of lines (called payload) with a
    metadata encoded in the comment that is put adjacent to it (for a single
    line fragment) or enclosing it (for a multiline fragment). Fragments are
    usually used in configuration files or code. Here is the structure of a
    single-line fragment:

     <payload> # <label> <attrs>

    Here is the structure of a multi-line fragment:

     # BEGIN <label> <attrs>
     <payload>
     # END <label> [<attrs>]

    Label is by default "FRAGMENT" but can be other string. Attributes are a
    sequence of "name=val" separated by whitespace, where name must be
    alphanums only and val is zero or more non-whitespace characters. There
    must at least be an attribute with name "id", it is used to identify
    fragment and allow the fragment to be easily replaced/modified/deleted
    from text. Attributes are optional in the ending comment.

    Comment character used is by default "#" ("shell"-style comment), but
    other comment styles are supported (see below).

    Examples of single-line fragments (the second example uses "c"-style
    comment and the third uses "cpp"-style comment):

     RSYNC_ENABLE=1 # FRAGMENT id=enable
     some text /* FRAGMENT id=id2 */
     some text // FRAGMENT id=id3 foo=1 bar=2

    An example of multi-line fragment (using "html"-style comment instead of
    "shell"):

     <!-- BEGIN FRAGMENT id=id4 -->
     some
     lines
     of
     text
     <!-- END FRAGMENT id=id4 -->

    Another example (using "ini"-style comment):

     ; BEGIN FRAGMENT id=default-settings
     register_globals=On
     extension=mysql.so
     extension=gd.so
     memory_limit=256M
     post_max_size=64M
     upload_max_filesize=64M
     browscap=/c/share/php/browscap.ini
     allow_url_fopen=0
     ; END FRAGMENT

FUNCTIONS
  delete_fragment
    Usage:

     delete_fragment(%args) -> [$status_code, $reason, $payload, \%result_meta]

    Delete fragment in text.

    If there are multiple occurences of fragment (which is considered an
    abnormal condition), all occurences will be deleted.

    Newline deletion behaviour: if fragment at the bottom of text does not
    end with newline (which is considered bad style), the text after the
    fragment is deleted will also not end with newline.

    This function is not exported by default, but exportable.

    Arguments ('*' denotes required arguments):

    *   comment_style => *str* (default: "shell")

        Comment style.

    *   id* => *str*

        Fragment ID.

    *   label => *str|code* (default: "FRAGMENT")

        Comment label.

    *   text* => *str*

        The text to delete fragment from.

    Returns an enveloped result (an array).

    First element ($status_code) is an integer containing HTTP-like status
    code (200 means OK, 4xx caller error, 5xx function error). Second
    element ($reason) is a string containing error message, or something
    like "OK" if status is 200. Third element ($payload) is the actual
    result, but usually not present when enveloped result is an error
    response ($status_code is not 2xx). Fourth element (%result_meta) is
    called result metadata and is optional, a hash that contains extra
    information, much like how HTTP response headers provide additional
    metadata.

    Return value: A hash of result (hash)

    Will return status 200 if operation is successful and text is deleted.
    The result is a hash with the following keys: "text" will contain the
    new text, "orig_payload" will contain the original fragment payload
    before being deleted, "orig_fragment" will contain the original
    fragment. If there are multiple occurences (which is considered an
    abnormal condition), only the last deleted fragment will be returned in
    "orig_payload" and "orig_fragment".

    Will return status 304 if nothing is changed (i.e. when the fragment
    that needs to be deleted already does not exist in the text).

  get_fragment
    Usage:

     get_fragment(%args) -> [$status_code, $reason, $payload, \%result_meta]

    Get fragment with a certain ID in text.

    If there are multiple occurences of the fragment with the same ID ,

    This function is not exported by default, but exportable.

    Arguments ('*' denotes required arguments):

    *   comment_style => *str* (default: "shell")

        Comment style.

    *   id* => *str*

        Fragment ID.

    *   label => *str* (default: "FRAGMENT")

        Comment label.

    *   text* => *str*

        The text which contain fragments.

    Returns an enveloped result (an array).

    First element ($status_code) is an integer containing HTTP-like status
    code (200 means OK, 4xx caller error, 5xx function error). Second
    element ($reason) is a string containing error message, or something
    like "OK" if status is 200. Third element ($payload) is the actual
    result, but usually not present when enveloped result is an error
    response ($status_code is not 2xx). Fourth element (%result_meta) is
    called result metadata and is optional, a hash that contains extra
    information, much like how HTTP response headers provide additional
    metadata.

    Return value: Fragment (array)

    Will return status 200 if fragment is found. Result will be a hash with
    the following keys: "raw" (string), "payload" (string), "attrs" (hash),
    "id" (string, can also be found in attributes).

    Return 404 if fragment is not found.

  insert_fragment
    Usage:

     insert_fragment(%args) -> [$status_code, $reason, $payload, \%result_meta]

    Insert or replace a fragment in text.

    Newline insertion behaviour: if fragment is inserted at the bottom and
    text does not end with newline (which is considered bad style), the
    inserted fragment will also not end with newline. Except when original
    text is an empty string, in which case an ending newline will still be
    added.

    This function is not exported by default, but exportable.

    Arguments ('*' denotes required arguments):

    *   attrs => *hash* (default: {})

        (No description)

    *   comment_style => *str* (default: "shell")

        Comment style.

    *   good_pattern => *str*

        Regex pattern which if found means fragment need not be inserted.

    *   id* => *str*

        Fragment ID.

    *   label => *str* (default: "FRAGMENT")

        Comment label.

    *   payload* => *str*

        Fragment content.

    *   replace_pattern => *str*

        Regex pattern which if found will be used for placement of fragment.

        If fragment needs to be inserted into file, then if
        "replace_pattern" is defined then it will be searched. If found,
        fragment will be placed to replace the pattern. Otherwise, fragment
        will be inserted at the end (or beginning, see "top_style") of file.

    *   text* => *str*

        The text to insert fragment into.

    *   top_style => *bool* (default: 0)

        Whether to append fragment at beginning of file instead of at the
        end.

        Default is false, which means to append at the end of file.

        Note that this only has effect if "replace_pattern" is not defined
        or replace pattern is not found in file. Otherwise, fragment will be
        inserted to replace the pattern.

    Returns an enveloped result (an array).

    First element ($status_code) is an integer containing HTTP-like status
    code (200 means OK, 4xx caller error, 5xx function error). Second
    element ($reason) is a string containing error message, or something
    like "OK" if status is 200. Third element ($payload) is the actual
    result, but usually not present when enveloped result is an error
    response ($status_code is not 2xx). Fourth element (%result_meta) is
    called result metadata and is optional, a hash that contains extra
    information, much like how HTTP response headers provide additional
    metadata.

    Return value: A hash of result (hash)

    Will return status 200 if operation is successful and text is changed.
    The result is a hash with the following keys: "text" will contain the
    new text, "orig_payload" will contain the original payload before being
    removed/replaced, "orig_fragment" will contain the original fragment (or
    the text that matches "replace_pattern").

    Will return status 304 if nothing is changed (i.e. if fragment with the
    same payload that needs to be inserted already exists in the text).

  list_fragments
    Usage:

     list_fragments(%args) -> [$status_code, $reason, $payload, \%result_meta]

    List fragments in text.

    This function is not exported by default, but exportable.

    Arguments ('*' denotes required arguments):

    *   comment_style => *str* (default: "shell")

        Comment style.

    *   label => *str* (default: "FRAGMENT")

        Comment label.

    *   text* => *str*

        The text which contain fragments.

    Returns an enveloped result (an array).

    First element ($status_code) is an integer containing HTTP-like status
    code (200 means OK, 4xx caller error, 5xx function error). Second
    element ($reason) is a string containing error message, or something
    like "OK" if status is 200. Third element ($payload) is the actual
    result, but usually not present when enveloped result is an error
    response ($status_code is not 2xx). Fourth element (%result_meta) is
    called result metadata and is optional, a hash that contains extra
    information, much like how HTTP response headers provide additional
    metadata.

    Return value: List of fragments (array)

    Will return status 200 if operation is successful. Result will be an
    array of fragments, where each fragment is a hash containing these keys:
    "raw" (string), "payload" (string), "attrs" (hash), "id" (string, can
    also be found in attributes).

  set_fragment_attrs
    Usage:

     set_fragment_attrs(%args) -> [$status_code, $reason, $payload, \%result_meta]

    Set/unset attributes of a fragment.

    If there are multiple occurences of the fragment with the same ID ,

    This function is not exported by default, but exportable.

    Arguments ('*' denotes required arguments):

    *   attrs* => *hash*

        To delete an attribute in the fragment, you can set the value to
        undef.

    *   comment_style => *str* (default: "shell")

        Comment style.

    *   id* => *str*

        Fragment ID.

    *   label => *str* (default: "FRAGMENT")

        Comment label.

    *   text* => *str*

        The text which contain fragments.

    Returns an enveloped result (an array).

    First element ($status_code) is an integer containing HTTP-like status
    code (200 means OK, 4xx caller error, 5xx function error). Second
    element ($reason) is a string containing error message, or something
    like "OK" if status is 200. Third element ($payload) is the actual
    result, but usually not present when enveloped result is an error
    response ($status_code is not 2xx). Fourth element (%result_meta) is
    called result metadata and is optional, a hash that contains extra
    information, much like how HTTP response headers provide additional
    metadata.

    Return value: New text and other data (array)

    Will return status 200 if fragment is found. Result will be a hash
    containing these keys: "text" (string, the modified text), "orig_attrs"
    (hash, the old attributes before being modified).

    Return 404 if fragment is not found.

FAQ
  Why is my fragment not found/listed?
     foo=1 # FRAGMENT seq=1

     # BEGIN FRAGMENT name=foo
     blah
     blah blah
     # END FRAGMENT name=foo

    You forgot to add the `id` attribute. It is required.

  What if I need to have whitespace in my attribute name?
    You can't. Attribute names need to be alphanums only.

  What if I need to have whitespace in my attribute value?
    You can encode, e.g. with URI encoding:

     foo=1 # FRAGMENT id=1 comment=very%20simple

  What if I forgot to properly close a multi-line fragment?
     # BEGIN FRAGMENT id=1
     blah

    Currently "list_fragments" or "get_fragment" will not find your fragment
    without emitting warning/error message.

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

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

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) 2025 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=Text-Fragment>

    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.

