Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nodePackages.quicktype: init at 15.0.258 #107168

Closed
wants to merge 1 commit into from

Conversation

evanjs
Copy link
Member

@evanjs evanjs commented Dec 18, 2020

Motivation for this change

https://app.quicktype.io/ generates strongly-typed models and serializers from JSON, JSON Schema, TypeScript, and GraphQL queries, making it a breeze to work with JSON type-safely in many programming languages.

Things Done
  • Add quicktype to node-packages.json
  • Run node-packages' generate.sh to update node packages

Example Usage

Example schema.json conversions

Source schema.json
{
  "$id": "https://example.com/person.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Person",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string",
      "description": "The person's first name."
    },
    "lastName": {
      "type": "string",
      "description": "The person's last name."
    },
    "age": {
      "description": "Age in years which must be equal to or greater than zero.",
      "type": "integer",
      "minimum": 0
    }
  }
}

C++

Invocation

quicktype -s schema --lang c++ schema.json > schema.cpp (schema.c++ would also work)
or
quicktype -s schema -o schema.cpp schema.json

Result

Contents of schema.cpp
//  To parse this JSON data, first install
//
//      Boost     http://www.boost.org
//      json.hpp  https://github.com/nlohmann/json
//
//  Then include this file, and then do
//
//     Schema data = nlohmann::json::parse(jsonString);

#pragma once

#include "json.hpp"

#include <boost/optional.hpp>
#include <stdexcept>
#include <regex>

#ifndef NLOHMANN_OPT_HELPER
#define NLOHMANN_OPT_HELPER
namespace nlohmann {
    template <typename T>
    struct adl_serializer<std::shared_ptr<T>> {
        static void to_json(json & j, const std::shared_ptr<T> & opt) {
            if (!opt) j = nullptr; else j = *opt;
        }

        static std::shared_ptr<T> from_json(const json & j) {
            if (j.is_null()) return std::unique_ptr<T>(); else return std::unique_ptr<T>(new T(j.get<T>()));
        }
    };
}
#endif

namespace quicktype {
    using nlohmann::json;

    class ClassMemberConstraints {
        private:
        boost::optional<int> min_value;
        boost::optional<int> max_value;
        boost::optional<size_t> min_length;
        boost::optional<size_t> max_length;
        boost::optional<std::string> pattern;

        public:
        ClassMemberConstraints(
            boost::optional<int> min_value,
            boost::optional<int> max_value,
            boost::optional<size_t> min_length,
            boost::optional<size_t> max_length,
            boost::optional<std::string> pattern
        ) : min_value(min_value), max_value(max_value), min_length(min_length), max_length(max_length), pattern(pattern) {}
        ClassMemberConstraints() = default;
        virtual ~ClassMemberConstraints() = default;

        void set_min_value(int min_value) { this->min_value = min_value; }
        auto get_min_value() const { return min_value; }

        void set_max_value(int max_value) { this->max_value = max_value; }
        auto get_max_value() const { return max_value; }

        void set_min_length(size_t min_length) { this->min_length = min_length; }
        auto get_min_length() const { return min_length; }

        void set_max_length(size_t max_length) { this->max_length = max_length; }
        auto get_max_length() const { return max_length; }

        void set_pattern(const std::string &  pattern) { this->pattern = pattern; }
        auto get_pattern() const { return pattern; }
    };

    class ClassMemberConstraintException : public std::runtime_error {
        public:
        ClassMemberConstraintException(const std::string &  msg) : std::runtime_error(msg) {}
    };

    class ValueTooLowException : public ClassMemberConstraintException {
        public:
        ValueTooLowException(const std::string &  msg) : ClassMemberConstraintException(msg) {}
    };

    class ValueTooHighException : public ClassMemberConstraintException {
        public:
        ValueTooHighException(const std::string &  msg) : ClassMemberConstraintException(msg) {}
    };

    class ValueTooShortException : public ClassMemberConstraintException {
        public:
        ValueTooShortException(const std::string &  msg) : ClassMemberConstraintException(msg) {}
    };

    class ValueTooLongException : public ClassMemberConstraintException {
        public:
        ValueTooLongException(const std::string &  msg) : ClassMemberConstraintException(msg) {}
    };

    class InvalidPatternException : public ClassMemberConstraintException {
        public:
        InvalidPatternException(const std::string &  msg) : ClassMemberConstraintException(msg) {}
    };

    void CheckConstraint(const std::string &  name, const ClassMemberConstraints & c, int64_t value) {
        if (c.get_min_value() != boost::none && value < *c.get_min_value()) {
            throw ValueTooLowException ("Value too low for " + name + " (" + std::to_string(value) + "<" + std::to_string(*c.get_min_value()) + ")");
        }

        if (c.get_max_value() != boost::none && value > *c.get_max_value()) {
            throw ValueTooHighException ("Value too high for " + name + " (" + std::to_string(value) + ">" + std::to_string(*c.get_max_value()) + ")");
        }
    }

    void CheckConstraint(const std::string &  name, const ClassMemberConstraints & c, const std::string &  value) {
        if (c.get_min_length() != boost::none && value.length() < *c.get_min_length()) {
            throw ValueTooShortException ("Value too short for " + name + " (" + std::to_string(value.length()) + "<" + std::to_string(*c.get_min_length()) + ")");
        }

        if (c.get_max_length() != boost::none && value.length() > *c.get_max_length()) {
            throw ValueTooLongException ("Value too long for " + name + " (" + std::to_string(value.length()) + ">" + std::to_string(*c.get_max_length()) + ")");
        }

        if (c.get_pattern() != boost::none) {
            std::smatch result;
            std::regex_search(value, result, std::regex( *c.get_pattern() ));
            if (result.empty()) {
                throw InvalidPatternException ("Value doesn't match pattern for " + name + " (" + value +" != " + *c.get_pattern() + ")");
            }
        }
    }

    inline json get_untyped(const json & j, const char * property) {
        if (j.find(property) != j.end()) {
            return j.at(property).get<json>();
        }
        return json();
    }

    inline json get_untyped(const json & j, std::string property) {
        return get_untyped(j, property.data());
    }

    template <typename T>
    inline std::shared_ptr<T> get_optional(const json & j, const char * property) {
        if (j.find(property) != j.end()) {
            return j.at(property).get<std::shared_ptr<T>>();
        }
        return std::shared_ptr<T>();
    }

    template <typename T>
    inline std::shared_ptr<T> get_optional(const json & j, std::string property) {
        return get_optional<T>(j, property.data());
    }

    class Schema {
        public:
        Schema() :
            age_constraint(0, boost::none, boost::none, boost::none, boost::none)
        {}
        virtual ~Schema() = default;

        private:
        std::shared_ptr<int64_t> age;
        ClassMemberConstraints age_constraint;
        std::shared_ptr<std::string> first_name;
        std::shared_ptr<std::string> last_name;

        public:
        /**
         * Age in years which must be equal to or greater than zero.
         */
        std::shared_ptr<int64_t> get_age() const { return age; }
        void set_age(std::shared_ptr<int64_t> value) { if (value) CheckConstraint("age", age_constraint, *value); this->age = value; }

        /**
         * The person's first name.
         */
        std::shared_ptr<std::string> get_first_name() const { return first_name; }
        void set_first_name(std::shared_ptr<std::string> value) { this->first_name = value; }

        /**
         * The person's last name.
         */
        std::shared_ptr<std::string> get_last_name() const { return last_name; }
        void set_last_name(std::shared_ptr<std::string> value) { this->last_name = value; }
    };
}

namespace nlohmann {
    void from_json(const json & j, quicktype::Schema & x);
    void to_json(json & j, const quicktype::Schema & x);

    inline void from_json(const json & j, quicktype::Schema& x) {
        x.set_age(quicktype::get_optional<int64_t>(j, "age"));
        x.set_first_name(quicktype::get_optional<std::string>(j, "firstName"));
        x.set_last_name(quicktype::get_optional<std::string>(j, "lastName"));
    }

    inline void to_json(json & j, const quicktype::Schema & x) {
        j = json::object();
        j["age"] = x.get_age();
        j["firstName"] = x.get_first_name();
        j["lastName"] = x.get_last_name();
    }
}

Haskell

Invocation

quicktype -s schema --lang haskell schema.json > schema.hs

Result

Contents of schema.hs
{-# LANGUAGE StrictData #-}
{-# LANGUAGE OverloadedStrings #-}

module QuickType
    ( Schema (..)
    , decodeTopLevel
    ) where

import Data.Aeson
import Data.Aeson.Types (emptyObject)
import Data.ByteString.Lazy (ByteString)
import Data.HashMap.Strict (HashMap)
import Data.Text (Text)
import Data.Vector (Vector)

{-| age:
Age in years which must be equal to or greater than zero.

firstName:
The person's first name.

lastName:
The person's last name.
-}
data Schema = Schema
    { ageSchema :: Maybe Int
    , firstNameSchema :: Maybe Text
    , lastNameSchema :: Maybe Text
    } deriving (Show)

decodeTopLevel :: ByteString -> Maybe Schema
decodeTopLevel = decode

instance ToJSON Schema where
    toJSON (Schema ageSchema firstNameSchema lastNameSchema) =
        object
        [ "age" .= ageSchema
        , "firstName" .= firstNameSchema
        , "lastName" .= lastNameSchema
        ]

instance FromJSON Schema where
    parseJSON (Object v) = Schema
        <$> v .:? "age"
        <*> v .:? "firstName"
        <*> v .:? "lastName"

Rust

Invocation

quicktype -s schema -o schema.rs schema.json
or
quicktype -s schema --lang rust schema.json > schema.rs

Contents of schema.rs
// Example code that deserializes and serializes the model.
// extern crate serde;
// #[macro_use]
// extern crate serde_derive;
// extern crate serde_json;
//
// use generated_module::[object Object];
//
// fn main() {
//     let json = r#"{"answer": 42}"#;
//     let model: [object Object] = serde_json::from_str(&json).unwrap();
// }

extern crate serde_derive;

#[derive(Serialize, Deserialize)]
pub struct Schema {
    /// Age in years which must be equal to or greater than zero.
    #[serde(rename = "age")]
    age: Option<i64>,

    /// The person's first name.
    #[serde(rename = "firstName")]
    first_name: Option<String>,

    /// The person's last name.
    #[serde(rename = "lastName")]
    last_name: Option<String>,
}

@makefu
Copy link
Contributor

makefu commented Dec 18, 2020

@GrahamcOfBorg build quicktype

@evanjs
Copy link
Member Author

evanjs commented Dec 18, 2020

@GrahamcOfBorg build quicktype

Would we need
@GrahamcOfBorg build nodePackages.quicktype
or will it work without the nodePackages set?

@makefu
Copy link
Contributor

makefu commented Dec 19, 2020

Right now @GrahamcOfBorg seems to be having issues right now. and indeed nodePackages.quicktype would have been correct

@SuperSandro2000
Copy link
Member

PR got combined with a few other node updates. Closing so that the update goes trough smoothly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants