Function: verb-request-spec-from-string

verb-request-spec-from-string is a natively compiled function defined in verb.el.

Signature

(verb-request-spec-from-string TEXT &optional METADATA)

Documentation

Create and return a request specification from string TEXT.

The text format for request specifications is the following:

[COMMENT]...
METHOD [URL]
[HEADER]...

[BODY]

Each COMMENT must start with "#" or ":" (see Org mode comments and headline property syntax). All comments will be discarded after being read (they are not part of the returned value). COMMENT may also be a blank line.

METHOD must be a method matched by verb--http-methods-regexp (that is, an HTTP method or the value of verb--template-keyword). Matching is case-insensitive.

URL must be a full URL, or a part of it. If present, the scheme must be "http" or "https". If the scheme is not present, the URL will be interpreted as a path, plus (if present) query string and fragment. Therefore, using just "example.org" (note no scheme present) as URL will result in a URL with its path set to "example.org", not as its host. URL may end in a backslash, in which case the following line will be appended to it (ignoring its leading whitespace). The process is repeated as long as the current line ends with a backslash.

Each HEADER must be in the form of KEY: VALUE. KEY must be a nonempty string, VALUE can be the empty string. HEADER may also start with
"#", in which case it will be ignored.

BODY can contain arbitrary data. Note that there must be a blank line between the HEADER list and BODY. If BODY contains a properly formatted Babel source block, the block beginning and end lines will be removed.

As a special case, if the text specification consists exclusively of comments and/or whitespace, or is the empty string, signal verb-empty-spec.

If TEXT does not conform to the request specification text format, signal an error.

Before returning the request specification, set its metadata to METADATA.

Source Code

;; Defined in /nix/store/8jnfn8w2mxcxbr2crbklcnija9m0zlkq-emacs-packages-deps/share/emacs/site-lisp/elpa/verb-20260818.1921/verb.el
(defun verb-request-spec-from-string (text &optional metadata)
  "Create and return a request specification from string TEXT.

The text format for request specifications is the following:

[COMMENT]...
METHOD [URL]
[HEADER]...

[BODY]

Each COMMENT must start with \"#\" or \":\" (see Org mode comments and
headline property syntax).  All comments will be discarded after being
read (they are not part of the returned value).  COMMENT may also be a
blank line.

METHOD must be a method matched by `verb--http-methods-regexp' (that
is, an HTTP method or the value of `verb--template-keyword').
Matching is case-insensitive.

URL must be a full URL, or a part of it.  If present, the scheme must
be \"http\" or \"https\".  If the scheme is not present, the URL will
be interpreted as a path, plus (if present) query string and fragment.
Therefore, using just \"example.org\" (note no scheme present) as URL
will result in a URL with its path set to \"example.org\", not as its
host.  URL may end in a backslash, in which case the following line
will be appended to it (ignoring its leading whitespace).  The process
is repeated as long as the current line ends with a backslash.

Each HEADER must be in the form of KEY: VALUE.  KEY must be a nonempty
string, VALUE can be the empty string.  HEADER may also start with
\"#\", in which case it will be ignored.

BODY can contain arbitrary data.  Note that there must be a blank
line between the HEADER list and BODY.  If BODY contains a properly
formatted Babel source block, the block beginning and end lines will
be removed.

As a special case, if the text specification consists exclusively of
comments and/or whitespace, or is the empty string, signal
`verb-empty-spec'.

If TEXT does not conform to the request specification text format,
signal an error.

Before returning the request specification, set its metadata to
METADATA."
  (let ((context (current-buffer))
        method url headers headers-start body backslash)
    (with-temp-buffer
      (insert text)
      (goto-char (point-min))

      ;;; COMMENTS + PROPERTIES

      ;; Skip initial blank lines, comments and properties.
      (while (and (re-search-forward "^\\s-*\\(\\(:\\|#\\).*\\)?$"
                                     (line-end-position) t)
                  (not (eobp)))
        (forward-char))
      ;; Check if the entire specification was just comments or empty.
      (when (string-empty-p (string-trim (buffer-substring (point)
                                                           (point-max))))
        ;; Signal `verb-empty-spec' if so
        (signal 'verb-empty-spec nil))

      ;;; METHOD + URL

      ;; Read HTTP method and URL line.
      ;; First, expand any code tags on it (if any):
      (let* ((case-fold-search t)
             (get-line-fn (lambda ()
                            (verb--eval-code-tags-in-string
                             (buffer-substring-no-properties
                              (point) (line-end-position))
                             context)))
             (line (funcall get-line-fn)))
        ;; Try to match:
        ;; A) METHOD URL
        ;; B) METHOD
        (if (string-match (concat "^\\s-*\\("
                                  (verb--http-methods-regexp)
                                  "\\)\\s-+\\(.+\\)$")
                          line)
            ;; A) Matched method + URL, store them.
            (progn
              (setq method (upcase (match-string 1 line))
                    url (string-remove-suffix "\\" (match-string 2 line)))

              ;; Subcase:
              ;; If URL ends with '\', append following lines to it
              ;; until one of them does not end with '\' (ignoring
              ;; leading whitespace, for alignment).
              (while (string-suffix-p "\\" line)
                (end-of-line)
                (if (eobp)
                    (user-error
                     "Backslash in URL not followed by additional line")
                  (forward-char))
                (back-to-indentation)
                (setq line (funcall get-line-fn))
                (when (string-empty-p line)
                  (user-error
                   "Backslash in URL not followed by additional content"))

                (setq backslash t)
                (setq url (concat url (string-remove-suffix "\\" line)))))

          (when (string-match (concat "^\\s-*\\("
                                      (verb--http-methods-regexp)
                                      "\\)\\s-*$")
                              line)
            ;; B) Matched method only, store it.
            (setq method (upcase (match-string 1 line))))))

      ;; We've processed the URL line, move to the end of it.
      (end-of-line)

      (if method
          (when (string= method verb--template-keyword)
            (setq method nil))
        (user-error (concat "Could not read a valid HTTP method (%s)\n"
                            "Additionally, you can also specify %s "
                            "(matching is case insensitive)")
                    (mapconcat #'identity verb--http-methods ", ")
                    verb--template-keyword))

      ;; Skip newline after URL line.
      (unless (eobp) (forward-char))
      (setq headers-start (point))

      ;;; HEADERS

      ;; Scan forward until we find a blank line (headers end).  In
      ;; the region covered, delete all lines starting with '#', as
      ;; these headers have been commented out.  Finally, go back to
      ;; where we started.
      (save-excursion
        (while (re-search-forward "^.+$" (line-end-position) t)
          (unless (eobp) (forward-char)))
        (delete-matching-lines "^[[:blank:]]*#" headers-start (point)))

      ;; Expand code tags in the rest of the buffer (if any).
      (save-excursion
        (verb--eval-code-tags-in-buffer (current-buffer) context))

      ;; Parse HTTP headers, stop as soon as we find a blank line.
      (while (re-search-forward "^\\(.+\\)$" (line-end-position) t)
        (let ((line (match-string 1)))
          ;; Check if line matches KEY: VALUE.
          (if (string-match verb-util--http-header-parse-regexp line)
              ;; Line matches, trim KEY and VALUE and store them.
              (push (cons (string-trim (match-string 1 line))
                          (string-trim (match-string 2 line)))
                    headers)
            (let ((msg (concat "Invalid HTTP header: \"%s\"\n"
                               "Make sure there's a blank line between"
                               " the headers and the request body")))
              (when (and backslash (string-match-p "^\\s-+" line))
                (setq msg (concat msg " (did you forget a backslash '\\'?)")))
              (user-error msg line))))
        (unless (eobp) (forward-char)))
      (setq headers (nreverse headers))

      ;;; BODY

      ;; Allow users to include Babel source blocks in their request
      ;; bodies. This allows them to have font locking and other
      ;; features that depend on specific content types (e.g JSON,
      ;; XML, etc.). Here we delete the source block delimiters so
      ;; that they are not included in the actual request.
      (delete-matching-lines "^[[:blank:]]*#\\+\\(begin\\|end\\)_src")

      ;; Skip blank line after headers.
      (unless (eobp) (forward-char))

      ;; The rest of the buffer is the request body.
      (let ((rest (buffer-substring (point) (point-max))))
        ;; Only read body if it isn't comprised entirely of
        ;; whitespace, but if it's not and has leading/trailing
        ;; whitespace, include it.
        (unless (string-empty-p (string-trim rest))
          ;; Now we know body isn't comprised entirely of whitespace,
          ;; check if the user wants to delete any trailing characters.
          (setq body (if verb-trim-body-end
                         (replace-regexp-in-string
                          (concat verb-trim-body-end "$") "" rest)
                       rest))))
      (when (buffer-local-value 'verb--multipart-boundary context)
        (verb-util--log nil 'W "Detected an unfinished multipart form"))
      ;; Return a `verb-request-spec'.
      (verb-request-spec :method method
                         :url (unless (string-empty-p (or url ""))
                                (verb--clean-url url))
                         :headers headers
                         :body body
                         :metadata metadata))))