{"action":"created","comment":{"url":"https://api.github.com/repos/bos/text-icu/pulls/comments/37683201","id":37683201,"diff_hunk":"@@ -0,0 +1,273 @@\n+{-# LANGUAGE BangPatterns, CPP, DeriveDataTypeable, ForeignFunctionInterface, OverloadedStrings #-}\n+-- |\n+-- Module      : Data.Text.ICU.Spoof\n+-- Copyright   : (c) 2015 Ben Hamilton\n+--\n+-- License     : BSD-style\n+-- Maintainer  : bgertzfield@gmail.com\n+-- Stability   : experimental\n+-- Portability : GHC\n+--\n+-- String spoofing (confusability) checks for Unicode, implemented as bindings to\n+-- the International Components for Unicode (ICU) uspoof library.\n+\n+module Data.Text.ICU.Spoof\n+    (\n+    -- * Unicode spoof checking API\n+    -- $api\n+    -- * Types\n+      MSpoof\n+    , Spoof\n+    , SpoofCheck(..)\n+    , SpoofCheckResult(..)\n+    , RestrictionLevel(..)\n+    , SkeletonTypeOverride(..)\n+    -- * Functions\n+    , open\n+    , openFromSerialized\n+    , openFromSource\n+    , getSkeleton\n+    , getChecks\n+    , setChecks\n+    , getRestrictionLevel\n+    , setRestrictionLevel\n+    , getAllowedLocales\n+    , setAllowedLocales\n+    , areConfusable\n+    , spoofCheck\n+    , serialize\n+    ) where\n+\n+#include <unicode/uspoof.h>\n+\n+import Control.Applicative\n+import Data.Bits ((.&.))\n+import Data.ByteString (ByteString)\n+import Data.ByteString.Internal (create, memcpy, toForeignPtr)\n+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)\n+import Data.Int (Int32)\n+import Data.List (intercalate)\n+import Data.Text (Text, pack, splitOn, strip, unpack)\n+import Data.Text.Foreign (fromPtr, useAsPtr)\n+import Data.Text.ICU.BitMask (ToBitMask, fromBitMask, highestValueInBitMask, toBitMask)\n+import Data.Text.ICU.Spoof.Internal (MSpoof, USpoof, Spoof, withSpoof, wrap, wrapWithSerialized)\n+import Data.Text.ICU.Error.Internal (UErrorCode, handleError, handleOverflowError)\n+import Data.Text.ICU.Internal (UChar)\n+import Data.Word (Word8)\n+import Foreign.C.String (CString, peekCString, withCString)\n+import Foreign.Ptr (Ptr, castPtr, nullPtr, plusPtr)\n+import Foreign.ForeignPtr (withForeignPtr)\n+\n+-- $api\n+--\n+\n+data SpoofCheck = SingleScriptConfusable\n+                | MixedScriptConfusable\n+                | WholeScriptConfusable\n+                | AnyCase\n+                | RestrictionLevel\n+                | Invisible\n+                | CharLimit\n+                | MixedNumbers\n+                | AllChecks\n+                | AuxInfo\n+                deriving (Bounded, Enum, Eq, Show)\n+\n+instance ToBitMask SpoofCheck where\n+  toBitMask SingleScriptConfusable = #const USPOOF_SINGLE_SCRIPT_CONFUSABLE\n+  toBitMask MixedScriptConfusable = #const USPOOF_MIXED_SCRIPT_CONFUSABLE\n+  toBitMask WholeScriptConfusable = #const USPOOF_WHOLE_SCRIPT_CONFUSABLE\n+  toBitMask AnyCase = #const USPOOF_ANY_CASE\n+  toBitMask RestrictionLevel = #const USPOOF_RESTRICTION_LEVEL\n+  toBitMask Invisible = #const USPOOF_INVISIBLE\n+  toBitMask CharLimit = #const USPOOF_CHAR_LIMIT\n+  toBitMask MixedNumbers = #const USPOOF_MIXED_NUMBERS\n+  toBitMask AllChecks = #const USPOOF_ALL_CHECKS\n+  toBitMask AuxInfo = #const USPOOF_AUX_INFO\n+\n+type USpoofCheck = Int32\n+\n+data RestrictionLevel = ASCII\n+                      | SingleScriptRestrictive\n+                      | HighlyRestrictive\n+                      | ModeratelyRestrictive\n+                      | MinimallyRestrictive\n+                      | Unrestrictive\n+                      deriving (Bounded, Enum, Eq, Show)\n+\n+instance ToBitMask RestrictionLevel where\n+  toBitMask ASCII = #const USPOOF_ASCII\n+  toBitMask SingleScriptRestrictive = #const USPOOF_SINGLE_SCRIPT_RESTRICTIVE\n+  toBitMask HighlyRestrictive = #const USPOOF_HIGHLY_RESTRICTIVE\n+  toBitMask ModeratelyRestrictive = #const USPOOF_MODERATELY_RESTRICTIVE\n+  toBitMask MinimallyRestrictive = #const USPOOF_MINIMALLY_RESTRICTIVE\n+  toBitMask Unrestrictive = #const USPOOF_UNRESTRICTIVE\n+\n+type URestrictionLevel = Int32\n+\n+data SpoofCheckResult = CheckOK\n+                      | CheckFailed [SpoofCheck]\n+                      | CheckFailedWithRestrictionLevel { checks :: [SpoofCheck],\n+                                                           level :: RestrictionLevel }\n+                deriving (Eq, Show)\n+\n+data SkeletonTypeOverride = SkeletonSingleScript\n+                          | SkeletonAnyCase\n+                          deriving (Bounded, Enum, Eq, Show)\n+\n+instance ToBitMask SkeletonTypeOverride where\n+  toBitMask SkeletonSingleScript = #const USPOOF_SINGLE_SCRIPT_CONFUSABLE\n+  toBitMask SkeletonAnyCase = #const USPOOF_ANY_CASE\n+\n+type USkeletonTypeOverride = Int32\n+\n+makeSpoofCheckResult :: USpoofCheck -> SpoofCheckResult\n+makeSpoofCheckResult c =\n+  case spoofChecks of\n+    [] -> CheckOK\n+    _ ->\n+      case restrictionLevel of\n+        Nothing -> CheckFailed spoofChecks\n+        Just l -> CheckFailedWithRestrictionLevel spoofChecks l\n+  where spoofChecks = fromBitMask $ fromIntegral $ c .&. #const USPOOF_ALL_CHECKS\n+        restrictionValue = c .&. #const USPOOF_RESTRICTION_LEVEL_MASK\n+        restrictionLevel = highestValueInBitMask $ fromIntegral $ restrictionValue\n+\n+-- | Open a spoof checker for checking Unicode strings for lookalike security issues.\n+open :: IO MSpoof\n+open = wrap =<< handleError uspoof_open\n+\n+-- | Open a spoof checker previously serialized to bytes using 'serialize'.\n+-- The ForeignPtr in the ByteString will remain referenced for the\n+-- lifetime of the returned object. The memory it points to must not\n+-- be modified until the returned object is closed.\n+openFromSerialized :: ByteString -> IO MSpoof\n+openFromSerialized b =\n+  case toForeignPtr b of\n+    (ptr, off, len) -> withForeignPtr ptr $ \\p ->\n+      wrapWithSerialized ptr =<< handleError (uspoof_openFromSerialized (p `plusPtr` off) (fromIntegral len) nullPtr)\n+\n+-- | Open a spoof checker given the contents of the \"confusables.txt\" and \"confusablesWholeScript.txt\"\n+-- files as described in Unicode UAX #39.\n+openFromSource :: ByteString -> ByteString -> IO MSpoof\n+openFromSource confusables confusablesWholeScript =\n+  unsafeUseAsCStringLen confusables $ \\(cptr, clen) ->\n+    unsafeUseAsCStringLen confusablesWholeScript $ \\(wptr, wlen) ->\n+      wrap =<< handleError (uspoof_openFromSource cptr (fromIntegral clen) wptr (fromIntegral wlen) nullPtr nullPtr)\n+\n+-- | Get the checks performed by a spoof checker.\n+getChecks :: MSpoof -> IO [SpoofCheck]\n+getChecks s = do\n+  withSpoof s $ \\sptr ->\n+    (fromBitMask . fromIntegral . (.&.) #{const USPOOF_ALL_CHECKS}) <$> handleError (uspoof_getChecks sptr)\n+\n+-- | Configure the checks performed by a spoof checker.\n+setChecks :: MSpoof -> [SpoofCheck] -> IO ()\n+setChecks s c = do\n+  withSpoof s $ \\sptr ->\n+    handleError $ uspoof_setChecks sptr . fromIntegral $ toBitMask c\n+\n+-- | Get the restriction level of a spoof checker.\n+getRestrictionLevel :: MSpoof -> IO (Maybe RestrictionLevel)\n+getRestrictionLevel s = do\n+  withSpoof s $ \\sptr ->\n+    (highestValueInBitMask . fromIntegral) <$> uspoof_getRestrictionLevel sptr\n+\n+-- | Configure the restriction level of a spoof checker.\n+setRestrictionLevel :: MSpoof -> RestrictionLevel -> IO ()\n+setRestrictionLevel s l = do\n+  withSpoof s $ \\sptr ->\n+    uspoof_setRestrictionLevel sptr . fromIntegral $ toBitMask l\n+\n+-- | Get the list of locale names allowed to be used with a spoof checker.\n+-- (We don't use LocaleName since the root and default locales have no meaning here.)","path":"Data/Text/ICU/Spoof.hsc","position":null,"original_position":183,"commit_id":"2055e3fe465bc5fcd700759194c8576da4ce17a0","original_commit_id":"72484dc95fe0c7c6155e211e9683d6746700ffe4","user":{"login":"bgertzfield","id":671827,"avatar_url":"https://avatars.githubusercontent.com/u/671827?v=3","gravatar_id":"","url":"https://api.github.com/users/bgertzfield","html_url":"https://github.com/bgertzfield","followers_url":"https://api.github.com/users/bgertzfield/followers","following_url":"https://api.github.com/users/bgertzfield/following{/other_user}","gists_url":"https://api.github.com/users/bgertzfield/gists{/gist_id}","starred_url":"https://api.github.com/users/bgertzfield/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bgertzfield/subscriptions","organizations_url":"https://api.github.com/users/bgertzfield/orgs","repos_url":"https://api.github.com/users/bgertzfield/repos","events_url":"https://api.github.com/users/bgertzfield/events{/privacy}","received_events_url":"https://api.github.com/users/bgertzfield/received_events","type":"User","site_admin":false},"body":"Cool, I figured there was an easy way to do that. I'll tidy up the docs.\r\n","created_at":"2015-08-21T22:12:14Z","updated_at":"2015-08-21T22:12:14Z","html_url":"https://github.com/bos/text-icu/pull/17#discussion_r37683201","pull_request_url":"https://api.github.com/repos/bos/text-icu/pulls/17","_links":{"self":{"href":"https://api.github.com/repos/bos/text-icu/pulls/comments/37683201"},"html":{"href":"https://github.com/bos/text-icu/pull/17#discussion_r37683201"},"pull_request":{"href":"https://api.github.com/repos/bos/text-icu/pulls/17"}}},"pull_request":{"url":"https://api.github.com/repos/bos/text-icu/pulls/17","id":42994429,"html_url":"https://github.com/bos/text-icu/pull/17","diff_url":"https://github.com/bos/text-icu/pull/17.diff","patch_url":"https://github.com/bos/text-icu/pull/17.patch","issue_url":"https://api.github.com/repos/bos/text-icu/issues/17","number":17,"state":"open","locked":false,"title":"New module Data.Text.ICU.Spoof","user":{"login":"bgertzfield","id":671827,"avatar_url":"https://avatars.githubusercontent.com/u/671827?v=3","gravatar_id":"","url":"https://api.github.com/users/bgertzfield","html_url":"https://github.com/bgertzfield","followers_url":"https://api.github.com/users/bgertzfield/followers","following_url":"https://api.github.com/users/bgertzfield/following{/other_user}","gists_url":"https://api.github.com/users/bgertzfield/gists{/gist_id}","starred_url":"https://api.github.com/users/bgertzfield/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bgertzfield/subscriptions","organizations_url":"https://api.github.com/users/bgertzfield/orgs","repos_url":"https://api.github.com/users/bgertzfield/repos","events_url":"https://api.github.com/users/bgertzfield/events{/privacy}","received_events_url":"https://api.github.com/users/bgertzfield/received_events","type":"User","site_admin":false},"body":"This is my first attempt at integrating ICU's Unicode spoof-checking library with `Data.Text.ICU`.\r\n\r\nThis library checks for nasty Unicode lookalikes:\r\n\r\n```\r\nλ import Data.Text\r\nλ import Data.Text.ICU\r\nλ areConfusable spoof (pack \"Hello\") (pack \"World\")\r\nCheckOK\r\nλ let cyrillic = \"\\x0410\\x0412\\x0421\\x0415\"\r\nλ :print cyrillic\r\ncyrillic = \"АВСЕ\"\r\nλ areConfusable spoof (pack \"ABCE\") (pack cyrillic)\r\nCheckFailed [MixedScriptConfusable,WholeScriptConfusable]\r\n```\r\n\r\nMore tests are better, but this is a nice start.\r\n\r\nC library: http://icu-project.org/apiref/icu4c/uspoof_8h.html\r\n\r\n","created_at":"2015-08-21T00:50:08Z","updated_at":"2015-08-21T22:12:14Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5151222cb2d8ce9f087fa29d2b7e219796b6ec34","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/bos/text-icu/pulls/17/commits","review_comments_url":"https://api.github.com/repos/bos/text-icu/pulls/17/comments","review_comment_url":"https://api.github.com/repos/bos/text-icu/pulls/comments{/number}","comments_url":"https://api.github.com/repos/bos/text-icu/issues/17/comments","statuses_url":"https://api.github.com/repos/bos/text-icu/statuses/2055e3fe465bc5fcd700759194c8576da4ce17a0","head":{"label":"bgertzfield:spoof","ref":"spoof","sha":"2055e3fe465bc5fcd700759194c8576da4ce17a0","user":{"login":"bgertzfield","id":671827,"avatar_url":"https://avatars.githubusercontent.com/u/671827?v=3","gravatar_id":"","url":"https://api.github.com/users/bgertzfield","html_url":"https://github.com/bgertzfield","followers_url":"https://api.github.com/users/bgertzfield/followers","following_url":"https://api.github.com/users/bgertzfield/following{/other_user}","gists_url":"https://api.github.com/users/bgertzfield/gists{/gist_id}","starred_url":"https://api.github.com/users/bgertzfield/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bgertzfield/subscriptions","organizations_url":"https://api.github.com/users/bgertzfield/orgs","repos_url":"https://api.github.com/users/bgertzfield/repos","events_url":"https://api.github.com/users/bgertzfield/events{/privacy}","received_events_url":"https://api.github.com/users/bgertzfield/received_events","type":"User","site_admin":false},"repo":{"id":41125151,"name":"text-icu","full_name":"bgertzfield/text-icu","owner":{"login":"bgertzfield","id":671827,"avatar_url":"https://avatars.githubusercontent.com/u/671827?v=3","gravatar_id":"","url":"https://api.github.com/users/bgertzfield","html_url":"https://github.com/bgertzfield","followers_url":"https://api.github.com/users/bgertzfield/followers","following_url":"https://api.github.com/users/bgertzfield/following{/other_user}","gists_url":"https://api.github.com/users/bgertzfield/gists{/gist_id}","starred_url":"https://api.github.com/users/bgertzfield/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bgertzfield/subscriptions","organizations_url":"https://api.github.com/users/bgertzfield/orgs","repos_url":"https://api.github.com/users/bgertzfield/repos","events_url":"https://api.github.com/users/bgertzfield/events{/privacy}","received_events_url":"https://api.github.com/users/bgertzfield/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bgertzfield/text-icu","description":"This package provides the Haskell Data.Text.ICU library, for performing complex manipulation of Unicode text.","fork":true,"url":"https://api.github.com/repos/bgertzfield/text-icu","forks_url":"https://api.github.com/repos/bgertzfield/text-icu/forks","keys_url":"https://api.github.com/repos/bgertzfield/text-icu/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bgertzfield/text-icu/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bgertzfield/text-icu/teams","hooks_url":"https://api.github.com/repos/bgertzfield/text-icu/hooks","issue_events_url":"https://api.github.com/repos/bgertzfield/text-icu/issues/events{/number}","events_url":"https://api.github.com/repos/bgertzfield/text-icu/events","assignees_url":"https://api.github.com/repos/bgertzfield/text-icu/assignees{/user}","branches_url":"https://api.github.com/repos/bgertzfield/text-icu/branches{/branch}","tags_url":"https://api.github.com/repos/bgertzfield/text-icu/tags","blobs_url":"https://api.github.com/repos/bgertzfield/text-icu/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bgertzfield/text-icu/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bgertzfield/text-icu/git/refs{/sha}","trees_url":"https://api.github.com/repos/bgertzfield/text-icu/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bgertzfield/text-icu/statuses/{sha}","languages_url":"https://api.github.com/repos/bgertzfield/text-icu/languages","stargazers_url":"https://api.github.com/repos/bgertzfield/text-icu/stargazers","contributors_url":"https://api.github.com/repos/bgertzfield/text-icu/contributors","subscribers_url":"https://api.github.com/repos/bgertzfield/text-icu/subscribers","subscription_url":"https://api.github.com/repos/bgertzfield/text-icu/subscription","commits_url":"https://api.github.com/repos/bgertzfield/text-icu/commits{/sha}","git_commits_url":"https://api.github.com/repos/bgertzfield/text-icu/git/commits{/sha}","comments_url":"https://api.github.com/repos/bgertzfield/text-icu/comments{/number}","issue_comment_url":"https://api.github.com/repos/bgertzfield/text-icu/issues/comments{/number}","contents_url":"https://api.github.com/repos/bgertzfield/text-icu/contents/{+path}","compare_url":"https://api.github.com/repos/bgertzfield/text-icu/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bgertzfield/text-icu/merges","archive_url":"https://api.github.com/repos/bgertzfield/text-icu/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bgertzfield/text-icu/downloads","issues_url":"https://api.github.com/repos/bgertzfield/text-icu/issues{/number}","pulls_url":"https://api.github.com/repos/bgertzfield/text-icu/pulls{/number}","milestones_url":"https://api.github.com/repos/bgertzfield/text-icu/milestones{/number}","notifications_url":"https://api.github.com/repos/bgertzfield/text-icu/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bgertzfield/text-icu/labels{/name}","releases_url":"https://api.github.com/repos/bgertzfield/text-icu/releases{/id}","created_at":"2015-08-21T00:30:44Z","updated_at":"2015-08-21T00:30:44Z","pushed_at":"2015-08-21T22:09:46Z","git_url":"git://github.com/bgertzfield/text-icu.git","ssh_url":"git@github.com:bgertzfield/text-icu.git","clone_url":"https://github.com/bgertzfield/text-icu.git","svn_url":"https://github.com/bgertzfield/text-icu","homepage":"","size":662,"stargazers_count":0,"watchers_count":0,"language":"Haskell","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"bos:master","ref":"master","sha":"bb0d0b3d25933f7183066a4a5862674c03d67df4","user":{"login":"bos","id":23374,"avatar_url":"https://avatars.githubusercontent.com/u/23374?v=3","gravatar_id":"","url":"https://api.github.com/users/bos","html_url":"https://github.com/bos","followers_url":"https://api.github.com/users/bos/followers","following_url":"https://api.github.com/users/bos/following{/other_user}","gists_url":"https://api.github.com/users/bos/gists{/gist_id}","starred_url":"https://api.github.com/users/bos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bos/subscriptions","organizations_url":"https://api.github.com/users/bos/orgs","repos_url":"https://api.github.com/users/bos/repos","events_url":"https://api.github.com/users/bos/events{/privacy}","received_events_url":"https://api.github.com/users/bos/received_events","type":"User","site_admin":false},"repo":{"id":1013693,"name":"text-icu","full_name":"bos/text-icu","owner":{"login":"bos","id":23374,"avatar_url":"https://avatars.githubusercontent.com/u/23374?v=3","gravatar_id":"","url":"https://api.github.com/users/bos","html_url":"https://github.com/bos","followers_url":"https://api.github.com/users/bos/followers","following_url":"https://api.github.com/users/bos/following{/other_user}","gists_url":"https://api.github.com/users/bos/gists{/gist_id}","starred_url":"https://api.github.com/users/bos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bos/subscriptions","organizations_url":"https://api.github.com/users/bos/orgs","repos_url":"https://api.github.com/users/bos/repos","events_url":"https://api.github.com/users/bos/events{/privacy}","received_events_url":"https://api.github.com/users/bos/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bos/text-icu","description":"This package provides the Haskell Data.Text.ICU library, for performing complex manipulation of Unicode text.","fork":false,"url":"https://api.github.com/repos/bos/text-icu","forks_url":"https://api.github.com/repos/bos/text-icu/forks","keys_url":"https://api.github.com/repos/bos/text-icu/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bos/text-icu/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bos/text-icu/teams","hooks_url":"https://api.github.com/repos/bos/text-icu/hooks","issue_events_url":"https://api.github.com/repos/bos/text-icu/issues/events{/number}","events_url":"https://api.github.com/repos/bos/text-icu/events","assignees_url":"https://api.github.com/repos/bos/text-icu/assignees{/user}","branches_url":"https://api.github.com/repos/bos/text-icu/branches{/branch}","tags_url":"https://api.github.com/repos/bos/text-icu/tags","blobs_url":"https://api.github.com/repos/bos/text-icu/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bos/text-icu/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bos/text-icu/git/refs{/sha}","trees_url":"https://api.github.com/repos/bos/text-icu/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bos/text-icu/statuses/{sha}","languages_url":"https://api.github.com/repos/bos/text-icu/languages","stargazers_url":"https://api.github.com/repos/bos/text-icu/stargazers","contributors_url":"https://api.github.com/repos/bos/text-icu/contributors","subscribers_url":"https://api.github.com/repos/bos/text-icu/subscribers","subscription_url":"https://api.github.com/repos/bos/text-icu/subscription","commits_url":"https://api.github.com/repos/bos/text-icu/commits{/sha}","git_commits_url":"https://api.github.com/repos/bos/text-icu/git/commits{/sha}","comments_url":"https://api.github.com/repos/bos/text-icu/comments{/number}","issue_comment_url":"https://api.github.com/repos/bos/text-icu/issues/comments{/number}","contents_url":"https://api.github.com/repos/bos/text-icu/contents/{+path}","compare_url":"https://api.github.com/repos/bos/text-icu/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bos/text-icu/merges","archive_url":"https://api.github.com/repos/bos/text-icu/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bos/text-icu/downloads","issues_url":"https://api.github.com/repos/bos/text-icu/issues{/number}","pulls_url":"https://api.github.com/repos/bos/text-icu/pulls{/number}","milestones_url":"https://api.github.com/repos/bos/text-icu/milestones{/number}","notifications_url":"https://api.github.com/repos/bos/text-icu/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bos/text-icu/labels{/name}","releases_url":"https://api.github.com/repos/bos/text-icu/releases{/id}","created_at":"2010-10-21T23:29:02Z","updated_at":"2015-08-02T15:54:16Z","pushed_at":"2015-08-21T22:09:47Z","git_url":"git://github.com/bos/text-icu.git","ssh_url":"git@github.com:bos/text-icu.git","clone_url":"https://github.com/bos/text-icu.git","svn_url":"https://github.com/bos/text-icu","homepage":"","size":786,"stargazers_count":11,"watchers_count":11,"language":"Haskell","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":8,"mirror_url":null,"open_issues_count":9,"forks":8,"open_issues":9,"watchers":11,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/bos/text-icu/pulls/17"},"html":{"href":"https://github.com/bos/text-icu/pull/17"},"issue":{"href":"https://api.github.com/repos/bos/text-icu/issues/17"},"comments":{"href":"https://api.github.com/repos/bos/text-icu/issues/17/comments"},"review_comments":{"href":"https://api.github.com/repos/bos/text-icu/pulls/17/comments"},"review_comment":{"href":"https://api.github.com/repos/bos/text-icu/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/bos/text-icu/pulls/17/commits"},"statuses":{"href":"https://api.github.com/repos/bos/text-icu/statuses/2055e3fe465bc5fcd700759194c8576da4ce17a0"}}}}