 (defn path->segments [path]
   (fs/split (strip-extension path)))
 
-(defn recursive-merge [a b]
-  (if (and (map? a) (map? b))
-    (merge-with recursive-merge a b)
-    (merge a b)))
+;;;; Parsing
 
-(defn parse-step [tree [name-segments data]]
-  (update-in tree name-segments recursive-merge data))
+(defn parse-common [data]
+  {:title (data "title")
+   :description (data "description")
+   :mapped-here (mapv #(hash-map :name %) (data "mapped-here"))
+   :tags (mapv #(hash-map :name %) (data "tags"))})
 
-;;;; Node tree
+(defn parse-project [data]
+  {:copyright (data "copyright")})
+
+(defn parse-requirement [data]
+  {})
+
+(defn parse-component [data]
+  {})
+
+(defn parse-work-item [data]
+  {})
+
+(defn parse-tag [data]
+  {})
+
+(def kind-parsers
+  {"project" parse-project
+   "requirement" parse-requirement
+   "component" parse-component
+   "work-item" parse-work-item
+   "tag" parse-tag})
+
+(def kind-aliases
+  {"project" ["project"]
+   "requirement" ["requirement" "req" "r"]
+   "component" ["component" "comp" "c"]
+   "work-item" ["work-item" "work" "w"]
+   "tag" ["tag" "t"]})
+
+(defn parse-kind [data]
+  (when (contains? data "kind")
+    (when-let [kind (get data "kind")]
+      (ffirst (filter #(some #{kind} (second %)) kind-aliases)))))
+
+(defn custard-node? [node]
+  (contains? node :kind))
 
 (defn process-down [node f ctx]
   (let [{:keys [node ctx]} (f node ctx)]

                (fn [children]
                  (mapv #(process-down % f ctx) children))))))

(defn set-kind [node _]
  (letfn [(normalize [kind]
            (let [aliases {"project" ["project"]
                           "requirement" ["requirement" "req" "r"]
                           "component" ["component" "comp" "c"]
                           "work-item" ["work-item" "work" "w"]
                           "tag" ["tag" "t"]}
                  res (or (->> aliases
                               (filter #(some #{kind} (second %)))
                               ffirst)
                          kind)]
              res))]
    {:node (if (contains? node "kind")
             (update node "kind" normalize)
             node)}))

(defn set-parent [node ctx]
  (let [parent ctx]
    {:node (assoc node :parent (or (:parent node)
                                   (:name parent)))
     :ctx (if (contains? node "kind") node parent)}))

(defn create-mapped-here-links [node _]
  {:node (update node "mapped-here"
                 (fn [mapped-here]
                   (mapv #(hash-map :name %) mapped-here)))})

(defn build-tree [data]
  (letfn [(build-children [name-segments data]
            (into []
                  (comp (filter #(map? (second %)))
                        (map (fn [[k v]]
                               (build-node (conj name-segments k) v))))
                  data))
          (build-node [name-segments data]
            (cond
              (map? data)
              (into {:name (str/join "/" name-segments)
                     :children (build-children name-segments data)}
                    (remove #(map? (second %)) data))))
          (build-step [root [name-segment data]]
            (update root :children conj
                    (build-node [name-segment] data)))]
    (let [root {:name [] :children ()}
          tree (reduce build-step root data)]
      (-> tree
          (process-down set-kind nil)
          (process-down set-parent nil)
          (process-down create-mapped-here-links nil)))))

(defn flatten-tree [node]
  (letfn [(collect-step [m node]
            (merge m (flatten-tree node)))]
    (reduce collect-step
            (if (contains? node "kind")
              {(:name node) node}
              {})
            (:children node))))

            [:node (:name node)])
          (node->link [node]
            {:name (:name node)})
          (parse-node [graph node]
            (let [ident (node->ident node)
                  linked-node (update node :children
                                           #(mapv node->link %))]
              (-> graph
                  (update :nodes conj ident)
                  (assoc-in ident linked-node))))
          (build-step [graph [name node]]
            (parse-node graph node))]
    (reduce build-step {:node {} :nodes []} flat-tree)))

(defn recursive-merge [a b]
