.. Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements.  See the NOTICE file
   distributed with this work for additional information
   regarding copyright ownership.  The ASF licenses this file
   to you under the Apache License, Version 2.0 (the
   "License"); you may not use this file except in compliance
   with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing,
   software distributed under the License is distributed on an
   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   KIND, either express or implied.  See the License for the
   specific language governing permissions and limitations
   under the License.

.. include:: ../../common.defs

.. highlight:: cpp
.. default-domain:: cpp

.. _cripts-convenience:

Convenience APIs
****************

To make Cripts even more approachable, a set of optional convenience APIs are added
to the core and top level name space. These APIs are not required to be used, and
are enabled by adding a define to your Cript file (or when compiling the Cript).
Making this addition optional allows users to choose whether they want to use
these convenience APIs or stick with the traditional Cripts API style.

The convenience APIs carry a small (very small) overhead, due to how Cripts defers
(or delays) initializations of objects until they are actually used. These new APIs
are designed to be as efficient as possible, but they do introduce potential conflicts
in the top level namespace. Here's a simple example of how to enable these APIs:

.. code-block:: cpp

   #define CRIPTS_CONVENIENCE_APIS 1

   #include <cripts/Preamble.hpp>

   do_remap()
   {
     urls.request.query.Keep({"foo", "bar"});
   }

   #include <cripts/Epilogue.hpp>

.. note::
   The convenience APIs must be enabled before including the Preamble header.
   This define affects the entire compilation unit, so use it consistently
   throughout your Cript file.

.. _cripts-convenience-toplevel:

Top level API additions
=======================

For the most common patterns, the top level API additions are ``client``, ``server``, and
``urls``. These all have sub-level API additions, as explained in the following table:

===========================   =============================================
Object                        Traditional API equivalent
===========================   =============================================
``client.request``            ``borrow cripts::Client::Request::Get()``
``client.response``           ``borrow cripts::Client::Response::Get()``
``client.connection``         ``borrow cripts::Client::Connection::Get()``
``client.url``                ``borrow cripts::Client::URL::Get()``
``server.request``            ``borrow cripts::Server::Request::Get()``
``server.response``           ``borrow cripts::Server::Response::Get()``
``server.connection``         ``borrow cripts::Server::Connection::Get()``
``cached.url``                ``borrow cripts::Cache::URL::Get()``
``cached.response``           ``borrow cripts::Cache::Response::Get()``
``urls.request``              ``borrow cripts::Client::URL::Get()``
``urls.pristine``             ``borrow cripts::Pristine::URL::Get()``
``urls.parent``               ``borrow cripts::Parent::URL::Get()``
``urls.remap.to``             ``borrow cripts::Remap::To::URL::Get()``
``urls.remap.from``           ``borrow cripts::Remap::From::URL::Get()``
===========================   =============================================

The use of these top-level objects are identical to how you would use them with the traditional
APIs. The following code shows both the traditional Cripts API and the new APIs in a simple
example:

.. code-block:: cpp

   // Traditional Cripts API
   do_remap()
   {
     borrow req = cripts::Client::Request::Get();
     borrow url = cripts::Client::URL::Get();

     url.query.Keep({"foo", "bar"});
     req["X-Foo"] = "bar";
   }

   // Convenience API, does not need the borrow statements
   do_remap()
   {
     urls.request.query.Keep({"foo", "bar"});
     client.request["X-Foo"] = "bar";
   }

.. note::
   Both ``client.url`` and ``urls.request`` refer to the same underlying object, which is
   ``cripts::Client::URL``. This means that any changes made to ``urls.request``
   will also be reflected in ``client.url`` and vice versa.

.. _cripts-convenience-macros:

Convenience macros
==================

In addition to the top-level APIs, a set of convenience macros are provided as well,
enabled with the same ``#define`` as above. The following macros have been added,
which again populate the top level namespace:

===========================   ====================================================================
Macro                         Traditional API equivalent
===========================   ====================================================================
``Regex(name, ...)``          ``static cripts::Matcher::PCRE name(...)``
``ACL(name, ...)``            ``static cripts::Matcher::Range::IP name(...)``
``StatusCode(code, ...)``     ``cripts::Error::Status::Set(code, ...)``
``CreateCounter(id, name)``   ``instance.metrics[id] = cripts::Metrics::Counter::Create(name)``
``CreateGauge(id, name)``     ``instance.metrics[id] = cripts::Metrics::Gauge::Create(name)``
``FilePath(name, path)``      ``static const cripts::File::Path name(path)``
``UniqueUUID()``              ``cripts::UUID::Unique::Get()``
``TimeNow()``                 ``cripts::Time::Local::Now()``
===========================   ====================================================================

These macros provide a more concise syntax for common operations. Here are some examples:

.. _cripts-convenience-macros-examples:

Regex and ACL Example
---------------------

.. code-block:: cpp

   #define CRIPTS_CONVENIENCE_APIS 1
   #include <cripts/Preamble.hpp>

   do_remap()
   {
     Regex(path_regex, "^/api/v([0-9]+)/(.*)$");
     ACL(internal_networks, {"192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12"});

     if (internal_networks.Match(client.connection.IP())) {
       if (path_regex.Match(urls.request.path)) {
         // Internal API access allowed
         client.request["X-API-Version"] = path_regex[1]; // First capture group
         client.request["X-API-Path"] = path_regex[2];    // Second capture group
       }
     } else {
       StatusCode(403, "Access denied");
     }
   }

   #include <cripts/Epilogue.hpp>

Metrics and File Example
------------------------

.. code-block:: cpp

   #define CRIPTS_CONVENIENCE_APIS 1
   #include <cripts/Preamble.hpp>

   do_create_instance()
   {
     CreateCounter(0, "requests.total");
     CreateGauge(1, "active.connections");
     FilePath(config_file, "/etc/ats/custom.conf");
   }

   do_remap()
   {
     instance.metrics[0]->Increment(); // Increment request counter

     auto uuid = UniqueUUID();
     auto timestamp = TimeNow();

     client.request["X-Request-ID"] = uuid;
     client.request["X-Timestamp"] = timestamp.Epoch();
   }

   #include <cripts/Epilogue.hpp>

.. _cripts-convenience-performance:

Performance Considerations
==========================

The convenience APIs are designed to have minimal overhead:

1. **Lazy Initialization**: Objects are only initialized when first accessed
2. **Reference Semantics**: The convenience objects are references to the same underlying objects
3. **Compile-time Macros**: Most convenience macros expand to the same code as traditional APIs

However, there are some considerations:

- **Namespace Pollution**: The convenience APIs add names to the global namespace
- **Debugging**: Stack traces may show convenience wrapper functions
- **Compatibility**: Code using convenience APIs requires the ``#define`` to compile
- **Performance**: While the overhead is minimal, it may not be suitable for performance-critical
  code due to the lazy initialization.

.. _cripts-convenience-best-practices:

Best Practices
==============

When using convenience APIs:

1. **Consistent Usage**: Either use convenience APIs throughout a Cript or stick to traditional APIs
2. **Documentation**: Comment when using convenience APIs for team clarity.
3. **Testing**: Test both with and without convenience APIs if maintaining compatibility.

Example of mixed usage (not recommended):

.. code-block:: cpp

   do_remap()
   {
     // Mixed usage - avoid this pattern!
     borrow req = cripts::Client::Request::Get(); // Traditional
     urls.request.query.Keep({"foo"});            // Convenience
   }

Better approach:

.. code-block:: cpp

   do_remap()
   {
     // Consistent convenience API usage
     client.request["X-Processed"] = "true";
     urls.request.query.Keep({"foo", "bar"});
   }

.. note::
   The ``StatusCode`` macro currently only works with the status code, the reason
   message can not be set or overridden. Fixing this will require a future change
   in the Traffic Server code base.
