| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- #pragma once
- #include <map>
- #include <optional>
- #include <jvalidate/detail/expect.h>
- #include <jvalidate/forward.h>
- #include <jvalidate/uri.h>
- namespace jvalidate {
- /**
- * @brief An Adapter-specific owning cache of documents that we need to load
- * from an external resource. Because Adapter objects do not actually own the
- * JSON objects that they wrap, we need some method of holding them in cache
- * to prevent any use-after-free issues.
- *
- * As you can see from the constructor chain of {@see jvalidate::Schema},
- * the user can either provide their own DocumentCache, which can then be shared
- * between multiple root Schemas. Alternatively, they can provide a URIResolver,
- * which is a function that takes a URI as input, a JSON as an out-parameter,
- * and returns a boolean indicating success.
- * If the URIResolver is provided, then we automatically construct a temporary
- * DocumentCache around it for use in building the Schema. If neither the
- * URIResolver nor a DocumentCache are provided, then we will be unable to
- * resolve any external documents (even those on-disk).
- */
- template <Adapter A> class DocumentCache {
- public:
- using value_type = typename A::value_type;
- private:
- URIResolver<A> resolve_;
- std::map<URI, value_type> cache_;
- public:
- /**
- * @brief Constructs an empty (read: cannot resolve anything) cache for
- * external documents. Because there is no URIResolver, this object will
- * always return a nullopt when trying to load anything.
- */
- DocumentCache() = default;
- /**
- * @brief Construct a new DocumentCache from the given URIResolver function/
- * function-object.
- *
- * @param resolve A function that consumes a URI and returns a boolean status
- * code and concrete JSON object that can be stored in the Adapter type A as
- * an out-parameter.
- * This function is under no oblications to load any specific schemes from
- * input URIs, so it is necessary to think carefully about the domain of
- * schema references that you will be working on when implementing it.
- * @see tests/selfvalidate_test.cxx#load_external_for_test for an example
- * supporting http requests with libcurl and file requests with fstreams.
- */
- DocumentCache(URIResolver<A> const & resolve) : resolve_(resolve) {}
- operator bool() const { return resolve_; }
- std::optional<A> try_load(URI const & uri) {
- // Short circuit - without a URIResolver, we can always return nullopt,
- // because this library doesn't promise to know how to load external
- // schemas from any source (including files).
- if (not resolve_) {
- return std::nullopt;
- }
- auto [it, created] = cache_.try_emplace(uri);
- if (created && not resolve_(uri, it->second)) {
- // Doing it this way skips out on a move operation for the JSON object,
- // which could be useful if someone is using a legacy JSON object type.
- // Since std::map promises stability we don't need to concern ourselves
- // with reference invalidation even in a multi-threaded context - although
- // this code is not threadsafe.
- cache_.erase(it);
- return std::nullopt;
- }
- return A(it->second);
- }
- };
- }
|