diff --git a/doerg/deps.edn b/doerg/deps.edn index 2c1cbff..f71a480 100644 --- a/doerg/deps.edn +++ b/doerg/deps.edn @@ -9,7 +9,8 @@ com.rpl/specter {:mvn/version "1.1.6"} lambdaisland/deep-diff2 {:mvn/version "2.12.219"} mvxcvi/clj-cbor {:mvn/version "1.1.1"} - ch.qos.logback/logback-classic {:mvn/version "1.1.3"}} + ch.qos.logback/logback-classic {:mvn/version "1.1.3"} + org.clojure/test.check {:mvn/version "1.1.3"}} :paths ["src" "resources" "test"] :aliases {:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}} diff --git a/doerg/src/net/deertopia/doerg/element.clj b/doerg/src/net/deertopia/doerg/element.clj index b48c387..8aae19d 100644 --- a/doerg/src/net/deertopia/doerg/element.clj +++ b/doerg/src/net/deertopia/doerg/element.clj @@ -1,5 +1,5 @@ (ns net.deertopia.doerg.element - (:refer-clojure :exclude [read-string]) + (:refer-clojure :exclude [read-string type]) (:require [babashka.fs :as fs] [babashka.process :as p] @@ -9,6 +9,7 @@ [clojure.set :as set] [clojure.spec.alpha :as s] [clojure.string :as str] + [clojure.test.check.generators :as gen] [clojure.tools.logging.readable :as lr] [clojure.zip :as z] [com.rpl.specter :as sp] @@ -288,7 +289,7 @@ [] acc)))))) -;;; Specs +;;; Specs (top-level) ;; Data taken from uniorg/index.d.ts @@ -334,23 +335,77 @@ (s/def ::contents-begin nat-int?) (s/def ::contents-end nat-int?) -(defmulti element-spec type) -(defmulti object-spec type) +(defmulti element-spec :type) +(defmulti object-spec :type) +(defmulti greater-element-spec :type) +(defmulti recursive-object-spec :type) -(s/def ::object (s/multi-spec object-spec type)) +(def ^:private nfe + "NFE — “no further expectations.” Used in sub-specs of `::element` + et al. for elements with no additional structure beyond that + provided by their parents." + (s/with-gen (constantly true) + (constantly (gen/return {})))) + +(defmacro ^:private with-parent-spec [multifn spec] + `(s/merge ~spec (s/multi-spec ~multifn :type))) + +(s/def ::object (s/multi-spec object-spec :type)) (s/def ::element - (s/and (dict ^:opt {:contents-begin ::contents-begin - :contents-end ::contents-end} - {:children (s/coll-of ::object :kind vector?) - :type ::element-type}) - (s/multi-spec element-spec type))) + (with-parent-spec element-spec + (dict ^:opt {:contents-begin ::contents-begin + :contents-end ::contents-end} + {:children (s/coll-of ::object :kind vector?) + :type ::element-type}))) + +(s/def ::greater-element + (with-parent-spec greater-element-spec + (dict {:contents-begin ::contents-begin + :contents-end ::contents-end + :children (s/coll-of ::object :kind vector?) + :type ::greater-element-type}))) + +(s/def ::recursive-object + (with-parent-spec recursive-object-spec + (dict {:contents-begin ::contents-begin + :contents-end ::contents-end + :children (s/coll-of ::object :kind vector?) + :type ::object-type}))) + +(s/def ::todo-keyword string?) +(s/def ::priority string?) +(s/def ::commented boolean?) +(s/def ::level nat-int?) +(s/def ::tags (s/coll-of string? :kind vector?)) + + +;;; Specs (specific objects) (defmethod object-spec "text" [_] (dict {:value string?})) -#_ -(s/def ::greater-element - (s/keys :req-un [::contents-begin - ::contents-end - (s/coll-of ::element-type vector?)])) +(defmethod recursive-object-spec "bold" [_] nfe) +(defmethod recursive-object-spec "italic" [_] nfe) +(defmethod recursive-object-spec "code" [_] nfe) + +(defmethod recursive-object-spec "verbatim" [_] + (dict {:value string?})) + + +;;; Specs (specific elements) + +(defmethod element-spec "headline" [_] + (dict {:todo-keyword (s/nilable ::todo-keyword) + :priority (s/nilable ::priority) + :level ::level + :commented ::commented + :raw-value string? + :tags ::tags})) + + +;;; Specs (specific greater elements) + +(defmethod greater-element-spec "org-data" [_] nfe) + +(defmethod greater-element-spec "section" [_] nfe)