             [puppetlabs.puppetdb.schema :refer [defn-validated]]
             [puppetlabs.puppetdb.utils :as utils]))
 
-(defn file-pattern
-  [entity]
-  (re-pattern (str "^" (.getPath (io/file utils/export-root-dir entity ".*\\.json")) "$")))
+(defn make-certname-matcher
+  "Returns a function that find a certname in the `file-path` for the
+  given `entity`, the function returns nil if the path is not
+  recognized"
+  [entity with-hash?]
+  (let [command-certname-regex (re-pattern
+                                (str "^"
+                                     (apply str
+                                            (map utils/regex-quote
+                                                 [utils/export-root-dir
+                                                  File/separatorChar
+                                                  entity
+                                                  File/separatorChar]))
 
-(def after-certname-length
-  "The format is <certname>-<date hash><suffix>. This is a count of
-  the characters after the certname"
-  (+ 1
-     40
-     (count export/file-suffix)))
+                                     "(.*)"
+                                     (when with-hash?
+                                       "\\-\\p{XDigit}{40}")
+                                     "\\.json$"))]
+    (fn [file-path]
+      (when-let [[_ certname-match] (re-matches command-certname-regex file-path)]
+        [entity certname-match]))))
 
-(def export-root-dir-length
-  (count utils/export-root-dir))
-
-(defn extract-certname [^String command-type ^String path]
-  (let [path-length (.length path)
-        prefix-length (+ export-root-dir-length
-                         (.length command-type)
-                         2)]
-    (subs path prefix-length (- path-length after-certname-length))))
-
-(defn extract-facts-certname [^String path]
-  (let [prefix-length (+ export-root-dir-length
-                         (.length "facts")
-                         2)]
-    (subs path prefix-length (- (.length path) (count export/file-suffix)))))
+(def command-matcher
+  (some-fn (make-certname-matcher "catalogs" true)
+           (make-certname-matcher "reports" true)
+           (make-certname-matcher "facts" false)))
 
 (defn-validated process-tar-entry
   "Determine the type of an entry from the exported archive, and process it

   This is a command-line tool for importing data into PuppetDB. It expects
   as input a tarball generated by the PuppetDB `export` command-line tool."
  (:import [org.apache.commons.compress.archivers.tar TarArchiveEntry]
           [puppetlabs.puppetdb.archive TarGzReader])
  (:require [clojure.java.io :as io]
            [clojure.tools.logging :as log]
            [puppetlabs.puppetdb.archive :as archive]

   tar-reader :- TarGzReader
   tar-entry :- TarArchiveEntry
   command-versions]
  (let [path (.getName tar-entry)
        [command-type certname] (command-matcher path)
        command-fn' (fn [command-kwd command-version]
                      (command-fn command-kwd
                                  command-version
                                  certname
                                  (-> tar-reader
                                      utils/read-json-content
                                      json/generate-string
                                      (.getBytes "UTF-8")
                                      java.io.ByteArrayInputStream.)))]
    (case command-type
      "catalogs"
      (do
        (log/infof "Importing catalog from archive entry '%s'" path)
        (command-fn' :replace-catalog
                     (:replace_catalog command-versions)))
      "reports"
      (do
        (log/infof "Importing report from archive entry '%s'" path)
        (command-fn' :store-report
                    (:store_report command-versions)))
      "facts"
      (do
        (log/infof "Importing facts from archive entry '%s'" path)
        (command-fn' :replace-facts
                     (:replace_facts command-versions)))
      nil)))

(def metadata-path
