diff --git a/src/axel_f/excel/query_select.cljc b/src/axel_f/excel/query_select.cljc new file mode 100644 index 0000000..9293117 --- /dev/null +++ b/src/axel_f/excel/query_select.cljc @@ -0,0 +1,50 @@ +(ns axel-f.excel.query-select + (:require [clojure.string :as string])) + +(def children (mapcat #(get % "children"))) + +(defn tag= [{:keys [tag]}] + (filter #(= tag (get % "tag")))) + +(defn attr= [{:keys [attr value]}] + (if (some? attr) + (filter #(= value + (get-in % ["attrs" attr]))) + identity)) + +(defn parse-item [item] + (if-some [[_ tag attr value] (re-matches #"(.*)\[(.*)=(.*)\]" item)] + {:tag tag + :attr attr + :value value} + {:tag item})) + +(defn make-tag-getter [query] + (->> (string/split query #"\.") + (map parse-item) + (map #(comp (tag= %) (attr= %) children)) + (apply comp))) + +(defn query-select [items query] + (let [query (or query "") + tag-getter (make-tag-getter query)] + (sequence tag-getter items))) + +(defn QUERYSELECT* + "Perform querySelector-like query on collection of items" + [^{:doc "Object representaiton of xml-like structure"} item + ^{:doc "Query to perform on `item`"} query] + (let [items (if (sequential? item) item [item])] + (query-select items query))) + +(def QUERYSELECT #'QUERYSELECT*) + +(def env + {"QUERYSELECT" QUERYSELECT}) + +(comment + (let [_ "2716902077id.successPASSresult.matchID Located" + parsed2 {"tag" "response","attrs" {"foo" "bar"} "children" [{"tag" "id-number", "children" ["2716902077\n "]} {"tag" "summary-result", "children" [{"tag" "key", "children" ["id.success\n "]} {"tag" "message", "children" ["PASS"]} {"tag" "message", "children" ["PASS22"]}]} {"tag" "results", "children" [{"tag" "key", "children" ["result.match\n "]} {"tag" "message", "children" ["ID Located\n "]}]}]} + items [parsed2] + query "response[foo=bar].summary-result.message"] + (query-select items query))) diff --git a/src/axel_f/excel_lite.cljc b/src/axel_f/excel_lite.cljc index 251f36c..4f3ff68 100644 --- a/src/axel_f/excel_lite.cljc +++ b/src/axel_f/excel_lite.cljc @@ -4,6 +4,7 @@ [axel-f.excel.coerce :as coerce] [axel-f.excel.operators :as operators] [axel-f.excel.collections :as collections] + [axel-f.excel.query-select :as query-select] [axel-f.excel.geo :as geo] [axel-f.excel.logic :as logic] [axel-f.excel.math :as math] @@ -17,6 +18,7 @@ (def base-env (merge operators/env + query-select/env collections/env geo/env logic/env diff --git a/test/axel_f/query_select_test.cljc b/test/axel_f/query_select_test.cljc new file mode 100644 index 0000000..688aff5 --- /dev/null +++ b/test/axel_f/query_select_test.cljc @@ -0,0 +1,37 @@ +(ns axel-f.query-select-test + (:require #?(:clj [clojure.test :as t] + :cljs [cljs.test :as t :include-macros true]) + [axel-f.excel :as af])) + +(t/deftest query-select + (let [xml {"tag" "response","attrs" {"foo" "bar"} "children" [{"tag" "id-number", "children" ["2716902077\n "]} {"tag" "summary-result", "children" [{"tag" "key", "children" ["id.success\n "]} {"tag" "message", "children" ["PASS"]} {"tag" "message", "children" ["PASS22"]}]} {"tag" "results", "children" [{"tag" "key", "children" ["result.match\n "]} {"tag" "message", "children" ["ID Located\n "]}]}]}] + (t/is (= ["PASS" "PASS22"] + ((af/compile "QUERYSELECT(xml, query)" + {"query" "response[foo=bar].summary-result.message" + "xml" xml})))) + (t/is (= ["PASS" "PASS22"] + ((af/compile "QUERYSELECT(xml, query)" + {"query" "response.summary-result.message" + "xml" xml})))) + (t/is (= [{"tag" "key", "children" ["id.success\n "]} + {"tag" "message", "children" ["PASS"]} + {"tag" "message", "children" ["PASS22"]}] + ((af/compile "QUERYSELECT(xml, query)" + {"query" "response.summary-result" + "xml" xml})))) + (t/is (= [] + ((af/compile "QUERYSELECT(xml, query)" + {"query" nil + "xml" xml})))) + (t/is (= [] + ((af/compile "QUERYSELECT(xml, query)" + {"query" "doesnt.exist" + "xml" xml})))) + (t/is (= [] + ((af/compile "QUERYSELECT(xml, query)" + {"query" "doesnt.exist" + "xml" xml})))) + (t/is (= [] + ((af/compile "QUERYSELECT(xml, query)" + {"query" "failed[fuuuuu.query" + "xml" xml}))))))