              {:content-type (mime/string->media-type "text/html;level=1")}) 2)
            ;; We get a specificty score of 3, indicating we matched on the
            ;; text/html rather than the preceeding text/*
-           1)))
-
-  ;; TODO: Test charsets
-
-  ;; TODO: Test encodings
-
-  )
+           1))))
+
+;; Charsets ------------------------------------
+
+(defn charset-acceptable? [rep acceptable-charset]
+  (when
+      (or (= (charset/charset acceptable-charset) "*")
+          (and
+           (some? (charset/charset acceptable-charset))
+           (= (charset/charset acceptable-charset)
+              (charset/charset rep)))
+          ;; Finally, let's see if their canonical names match
+          (and
+           (some? (charset/canonical-name acceptable-charset))
+           (= (charset/canonical-name acceptable-charset)
+              (charset/canonical-name rep))))
+    [(:quality acceptable-charset) (:quality rep)]))
+
+(defn highest-charset-quality
+  "Given a collection of acceptable charsets, return a function that
+  will return the quality."
+  [accepts]
+  (fn [rep]
+    (best (map (partial charset-acceptable? (:charset rep)) accepts))))
+
+(defn make-charset-quality-assessor
+  [req k]
+  (->
+   (->> (get-in req [:headers "accept-charset"]) parse-csv (map charset/to-charset-map))
+   highest-charset-quality
+   (wrap-quality-assessor :charset)
+   skip-rejected))
+
+(defn- get-highest-charset-quality
+  "Given the request and a representation, get the highest possible
+  quality value. Convenience function for independent testing."
+  [req rep]
+  (let [k :charset
+        qa (make-charset-quality-assessor req k)
+        rep (qa rep)]
+    (or (get-in rep [:qualities k])
+        (when (:rejected rep) :rejected))))
+
+(deftest charset-test
+
+  (testing "Basic match"
+    (is (= (get-highest-charset-quality
+            {:headers {"accept-charset" "utf-8"}}
+            {:charset (charset/to-charset-map "utf-8")})
+           [(float 1.0) (float 1.0)])))
+
+  (testing "Quality values"
+    (is (= (get-highest-charset-quality
+            {:headers {"accept-charset" "utf-8;q=0.8"}}
+            {:charset (charset/to-charset-map "utf-8;q=0.9")})
+           [(float 0.8) (float 0.9)])))
+
+  (testing "Multiple choices"
+    (is (= (get-highest-charset-quality
+            {:headers {"accept-charset" "us-ascii,utf-8;q=0.9,Shift_JIS"}}
+            {:charset (charset/to-charset-map "utf-8")})
+           [(float 0.9) (float 1.0)])))
+
+  (testing "Multiple choices but none ok"
+    (is (= (get-highest-charset-quality
+            {:headers {"accept-charset" "us-ascii,Shift_JIS"}}
+            {:charset (charset/to-charset-map "utf-8")})
+           :rejected))))
+
+;; TODO: Test encodings - note that encodings can be combined
+
+;; TODO: Test languages
+
+
 
 ;; "A request without any Accept header
 ;; field implies that the user agent

            {:content-type (mime/string->media-type "text/html")})
           [(float 1.0) 3 0 (float 1.0)])))


  (testing "Basic match, with multiple options"
    (is (= (get-highest-content-type-quality
            {:headers {"accept" "image/png,text/html"}}
            {:content-type (mime/string->media-type "text/html")})
           [(float 1.0) 3 0 (float 1.0)]))

    (testing "Basic match, with multiple options and q values"
      (is (= (get-highest-content-type-quality
              {:headers {"accept" "image/png,text/html;q=0.9"}}
              {:content-type (mime/string->media-type "text/html;q=0.8")})
             [(float 0.9) 3 0 (float 0.8)]))))


  (testing "Basic reject"
    (is (= (get-highest-content-type-quality
            {:headers {"accept" "text/html"}}
            {:content-type (mime/string->media-type "text/plain")})
           :rejected)))


  (testing "Basic reject with multiple options"
    (is (= (get-highest-content-type-quality

  ;; limited in the same fashion." -- RFC 7231 Section 5.3.1
  [rep acceptable]
  (when
      ;; TODO: Case sensitivity/insensitivity requirements
      (= (:parameters acceptable) (:parameters rep))
    (cond
      (and (= (:type acceptable) (:type rep))
