{ ce.a68 - Communication with a Compiler Explorer instance.

  Copyright (C) 2026 Jose E. Marchesi

  This program 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.

  This program 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 this program.  If not, see
  <https://www.gnu.org/licenses/>. }

module CE = access Utils, JSON, HTTP, Globals
def
    { The following variables should be set by the user of this
      library to whatever host and port to use when talking to a
      Compiler Explorer instance. }

    pub string ce_host = getenv("GODCC_CEHOST"),
        int ce_port = atoi(getenv("GODCC_CEPORT"), 10);

    { Communication with Compiler Explorer is performed via a socket. }

    int ce_socket := -1;

    { Make the HTTP subsystem to use "error" to report errors. }

    http_error := error;

    { Connect to the CE instance. }

    pub proc ce_connect = void:
    begin (ce_socket > -1 | error("socket already open"));
          if (ce_socket := fconnect(ce_host, ce_port)) = -1
          then errorno("connecting to " + ce_host + ":" + itoa(ce_port, 10))
          fi
    end;

    { Disconnect from the CE instance. }

    pub proc ce_disconnect = void:
    begin (ce_socket = -1 | error("socket not open"));
          if fclose(ce_socket) = -1
          then errorno("closing socket") fi;
          ce_socket := -1
    end;

    { Perform a GET operation in the CE instance, and return a JSON
      value containing the result of the operation.

      "what" is a string identifying the operation, which results in
      an /api/WHAT path.

      This procedure shall only be invoked between calls to ce_connect
      and ce_disconnect.

      See the Compiler Explorer documentation for a list of supported
      operations, and the form of JSON returned for the particular
      operations. }

    pub proc ce_get_json = (string what) JSON_Val:
    begin
          JSON_Val val;
          proc response = (int e, n, fd) void:
             (assert (e = 0); val := json_from_fd(n, fd, error));

          http_get(ce_socket, "/api/" + what,
                   (("Host", "godbolt.org"),
                    ("user-agent", "curl/7.74.0"),
                    ("content-type", "application/json"),
                    ("Accept", "application/json")),
                   response, false { fragmented  });
          val
    end;

    { Perform a POST operation in the CE instance, sending the given
      VAL, and return a JSON value containing the result of the
      operation.

      "what" is a string identifying the operation, which results in
      an /api/WHAT path.

      This procedure shall only be invoked between calls to ce_connect
      and ce_disconnect.

      See the Compiler Explorer documentation for a list of supported
      operations, and the form of JSON returned for the particular
      operations. }

    pub proc ce_post_json = (string what, JSON_Val val) JSON_Val:
    begin
          JSON_Val out_val;
          proc inh = (int e, n, fd) void:
             (if e /= 0 then error("received JSON in chunks") fi;
              out_val := json_from_fd(n, fd, error));

          http_post(ce_socket, "/api/" + what,
                    (("Host", "godbolt.org"),
                     ("user-agent", "curl/7.74.0"),
                     ("content-type", "application/json"),
                     ("Accept", "application/json")),
                    json_to_string(val),
                    inh, false { fragmented  });
          out_val
    end;

    { Perform a GET operation in the CE instance.

      "what" is a string identifying the operation, which results in
      an /api/WHAT path.

      See the documentation of http_post in http.a68 for information
      about the RESPONSE argument.

      This procedure shall only be invoked between calls to ce_connect
      and ce_disconnect. }

    pub proc ce_get_text = (string what,
                            proc(int,int,int)void response) void:
       http_get(ce_socket, "/api/" + what,
                (("Host", "godbolt.org"),
                 ("user-agent", "curl/7.74.0"),
                 ("Accept", "text/*")),
                response, true { fragmented  });

    { Perform a POST operation in the CE instance.

      "what" is a string identifying the operation, which results in
      an /api/WHAT path.

      See the documentation of http_post in http.a68 for information
      about the CONTENTS and RESPONSE arguments.

      This procedure shall only be invoked between calls to ce_connect
      and ce_disconnect.

      See the Compiler Explorer documentation for a list of supported
      operations, and the form of JSON returned for the particular
      operations. }

    pub proc ce_post_text = (string what,
                             union(string,proc(ref bool)string) contents,
                             proc(int,int,int)void response) void:
       http_post(ce_socket, "/api/" + what,
                 (("Host", "godbolt.org"),
                  ("user-agent", "curl/7.74.0"),
                  ("Accept", "text/*")),
                 contents, response, true { fragmented  });

    { Error with errno. }

    proc errorno = (string msg) void:
       (error(msg + ": " + strerror (errno)));

    skip
fed
