Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021  Stefan Vargyas

This file is part of Json-Type.

Json-Type is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Json-Type is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Json-Type.  If not, see <http://www.gnu.org/licenses/>.

--------------------------------------------------------------------------------

1. Json-Type's Filtering Dynamic Libraries
2. The Public Interface of Filter Libraries


1. Json-Type's Filtering Dynamic Libraries
==========================================

Json-Type's filtering dynamic libraries are shared libraries of a preestablished
form to be loaded dynamically at run time by the Json-Type's main program 'json'
upon specifications given by the user:

  $ json --help
  usage: json [ACTION|OPTION]... [FILE] [-- FILTER_LIB [FILTER_ARG]...]...
  ...
    -F|--filterlib-exec          run 'json-filterlib': execute the filter library
                                   specified by the first command line argument
                                   group of form `-- FILTER_LIB [FILTER_ARG]...';
                                   if the invoking command line specifies an input
                                   file name (prior to the first `--'), then take
                                   that to be library name and load the library by
                                   passing to it the argument `--help' only
   ...
    -f|--[no-]filter-libs        build a JSON input filtering chain using the
                                   specified filter libraries and arguments or,
                                   otherwise, do no filter the input at all;
                                   the filtering chain is specified as a series
                                   of one or more command line arguments groups
                                   of form `-- FILTER_LIB [FILTER_ARG]...'; the
                                   filtering series is placed immediately after
                                   the main program's command line arguments
   ...

The `--help' excerpt text above indicates that the user is able to construct a
filtering pipeline -- much like the Unix command line pipelines -- made of named
filtering libraries along with own command line arguments. The filter libraries
interpose between Json-Type's input and output classes and are able to apply any
kind of processing they implement.

Any filter library has two distinct modes of operation: an obvious 'filter' mode
and an 'exec' mode. In 'filter' mode, the purpose of the library is to be part of
a filtering pipeline chain along with other filter libraries running in the same
mode. The command line options of 'json' that dictate this mode of operation are
`-f|--filter-libs'.

In 'exec' mode, a filter library does not function as a filter component part of
a pipeline chain, but is ran on its own by 'json' much like a Unix filter program
that runs standalone. The 'exec' mode of operation makes a filter library to act
solely on the command line options given to it: for example is able to respond to
action options like `--version' or `--help'. The 'json' program invokes a filter
library in 'exec' mode when issued with action options `-F|--filterlib-exec'.

To exemplify these modes of operations, let run a couple of command lines using
the 'lib/test-filter.so' library which is part of the test suite of Json-Type.
Note that this library is build as result of running 'make' successfully in the
top directory of the source tree or when issuing the following two commands:

  $ cd lib

  $ make -f test-filter.mk

First, obtain the `--version' and `--help' texts from 'test-filter.so':

  $ json -F -- ./test-filter.so --version
  test-filter.so: version 0.1 -- 2018-06-28 13:44
  test-filter.so: json filter version: 0.1.0

  $ json -F -- ./test-filter.so --help
  usage: test-filter.so [ACTION|OPTION]...
  where actions are specified as:
    -G|--generic         print out the text 'generic action'
  the options are:
    -f|--fail=WHERE      set where the filter to fail or make
       --no-fail           it to not fail at all; WHERE can be
                           any of: 'init', 'handler' or 'exec'
    -d|--dump-options    print parsed options and exit
    -v|--version         print version numbers and exit
    -?|--help            display this help info and exit

The action option `-G|--generic' is no more then a dummy action this library is
implementing. The options `-f|--fail' makes the library simulate a failure --
producing an error message -- at either its initialization time (which occur
both in 'exec' and 'filter' mode), in 'filter' mode when first running a JSON
element handler function or when invoked in 'exec' mode.

  $ json -F -- ./test-filter.so -G
  test-filter.so: generic action

  $ json -F -- ./test-filter.so --fail=init
  json: error: ./test-filter.so: filter library: init failure

  $ json -f -- ./test-filter.so --fail=init
  json: error: ./test-filter.so: filter library: init failure

  $ json -F -- ./test-filter.so --fail=exec
  json: error: ./test-filter.so: filter library: exec failure

  $ echo '{"foo":"bar"}'|json -Jf -- ./test-filter.so -- ./test-filter.so
  /foo=bar

  $ echo '{}'|json -f -- ./test-filter.so -- ./test-filter.so --fail=handler
  json: error: <stdin>: filter library #2: handler failure

Note that the last command line above invoked a filter pipeline chain with two
instances of 'test-filter.so', of which the second one was instructed to fail.


2. The Public Interface of Filter Libraries
===========================================

For to fit into the filtering libraries framework described above each and every
filter library must implement the public interface defined by the include file
'lib/json-filter.h'. Specifically is about a set of C functions with particular
names and signatures:

  $ gcc -D__JSON_H -DJSON_FILTER_NAME=foo json-filter.h -E -o -
  ...
  JSON_API size_t foo_filter_get_version(void);

  JSON_API struct foo_filter_t* foo_filter_create_filter(
    const struct json_filter_options_t* opts,
    const struct json_handler_obj_t* handler,
    const struct json_handler_t** filter);

  JSON_API struct foo_filter_t* foo_filter_create_exec(
    const struct json_filter_options_t* opts);

  JSON_API void foo_filter_destroy(struct foo_filter_t*);

  JSON_API bool foo_filter_get_is_error(struct foo_filter_t*);

  JSON_API struct json_error_pos_t foo_filter_get_error_pos(
    struct foo_filter_t*);

  JSON_API const struct json_file_info_t* foo_filter_get_error_file(
    struct foo_filter_t*);

  JSON_API void foo_filter_print_error_desc(struct foo_filter_t*,
    FILE*);

  JSON_API bool foo_filter_exec(struct foo_filter_t*);

This public interface was designed for each filter library to be able to create
and run several instances of stateful filter classes.

For one to obtain say 'foo-filter.so' filter library is sufficient to have a C
source file ('foo-filter.c') that has the following C preprocessor directives
somewhere at its beginning:

  #define   JSON_FILTER_NAME foo
  #include "json-filter.h"

and which is implementing each of the C functions listed above. From the point
of view of 'json' program -- which is responsible for loading and running the
filter library 'foo-filter.so' -- the struct 'foo_filter_t' is entirely opaque.

The two 'create' functions do create and initialize 'foo_filter_t' structures,
corresponding to the currently ran instance of 'foo-filter.so'. One must note
that 'foo-filter.so' will most likely implement a stateful struct 'foo_filter_t'
-- thus each call to functions 'foo_filter_create_{filter,exec}' will have to
return pointers to different instances of struct 'foo_filter_t'.

A good starting point for the implementation of a new filter library is the two
files 'lib/test-filter.{c,mk}' -- from which 'lib/test-filter.so' is obtained.

For what concerns the filter library 'lib/test-filter.so', note that the library
is executable by itself:

  $ ./test-filter.so
  test-filter.so: version 0.1 -- 2018-06-28 13:44
  test-filter.so: json filter version: 0.1.0

This is so because on ELF platforms, one is permitted to have a function like
'test_filter_main' is at the end of 'lib/test-filter.c' that is called upon when
the encompassing ELF shared object is executed by itself as above.


