-- A subset of the Share API which we expose as MCP tools
module Unison.MCP.Share.API
  ( shareSearch,
    shareProjectReadme,
    ReadmeResponse (..),
  )
where

import Data.Aeson (FromJSON)
import Data.Aeson qualified as Aeson
import Data.Proxy (Proxy (..))
import Servant.API
import Servant.Client qualified as Servant
import Unison.Auth.HTTPClient (AuthenticatedHttpClient (..))
import Unison.Prelude
import Unison.Share.Codeserver qualified as Codeserver
import Unison.Share.Types (codeserverBaseURL)

shareSearch ::
  AuthenticatedHttpClient ->
  Text ->
  (IO (Either Servant.ClientError Aeson.Value))
shareSearch :: AuthenticatedHttpClient -> Text -> IO (Either ClientError Value)
shareSearch AuthenticatedHttpClient
authedHTTPClient Text
query = AuthenticatedHttpClient
-> ClientM Value -> IO (Either ClientError Value)
forall a.
AuthenticatedHttpClient -> ClientM a -> IO (Either ClientError a)
runClientM AuthenticatedHttpClient
authedHTTPClient (ClientM Value -> IO (Either ClientError Value))
-> ClientM Value -> IO (Either ClientError Value)
forall a b. (a -> b) -> a -> b
$ Maybe Text -> ClientM Value
httpSearch (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
query)

shareProjectReadme ::
  AuthenticatedHttpClient ->
  Text ->
  Text ->
  (IO (Either Servant.ClientError ReadmeResponse))
shareProjectReadme :: AuthenticatedHttpClient
-> Text -> Text -> IO (Either ClientError ReadmeResponse)
shareProjectReadme AuthenticatedHttpClient
authedHTTPClient Text
ownerHandle Text
projectSlug =
  AuthenticatedHttpClient
-> ClientM ReadmeResponse -> IO (Either ClientError ReadmeResponse)
forall a.
AuthenticatedHttpClient -> ClientM a -> IO (Either ClientError a)
runClientM AuthenticatedHttpClient
authedHTTPClient (ClientM ReadmeResponse -> IO (Either ClientError ReadmeResponse))
-> ClientM ReadmeResponse -> IO (Either ClientError ReadmeResponse)
forall a b. (a -> b) -> a -> b
$ Text -> Text -> ClientM ReadmeResponse
httpProjectReadme Text
ownerHandle Text
projectSlug

data ReadmeResponse = ReadmeResponse
  { ReadmeResponse -> Text
markdownReadMe :: Text
  }
  deriving (Int -> ReadmeResponse -> ShowS
[ReadmeResponse] -> ShowS
ReadmeResponse -> String
(Int -> ReadmeResponse -> ShowS)
-> (ReadmeResponse -> String)
-> ([ReadmeResponse] -> ShowS)
-> Show ReadmeResponse
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ReadmeResponse -> ShowS
showsPrec :: Int -> ReadmeResponse -> ShowS
$cshow :: ReadmeResponse -> String
show :: ReadmeResponse -> String
$cshowList :: [ReadmeResponse] -> ShowS
showList :: [ReadmeResponse] -> ShowS
Show, ReadmeResponse -> ReadmeResponse -> Bool
(ReadmeResponse -> ReadmeResponse -> Bool)
-> (ReadmeResponse -> ReadmeResponse -> Bool) -> Eq ReadmeResponse
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ReadmeResponse -> ReadmeResponse -> Bool
== :: ReadmeResponse -> ReadmeResponse -> Bool
$c/= :: ReadmeResponse -> ReadmeResponse -> Bool
/= :: ReadmeResponse -> ReadmeResponse -> Bool
Eq, (forall x. ReadmeResponse -> Rep ReadmeResponse x)
-> (forall x. Rep ReadmeResponse x -> ReadmeResponse)
-> Generic ReadmeResponse
forall x. Rep ReadmeResponse x -> ReadmeResponse
forall x. ReadmeResponse -> Rep ReadmeResponse x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. ReadmeResponse -> Rep ReadmeResponse x
from :: forall x. ReadmeResponse -> Rep ReadmeResponse x
$cto :: forall x. Rep ReadmeResponse x -> ReadmeResponse
to :: forall x. Rep ReadmeResponse x -> ReadmeResponse
Generic)

instance FromJSON ReadmeResponse where
  parseJSON :: Value -> Parser ReadmeResponse
parseJSON = do
    String
-> (Object -> Parser ReadmeResponse)
-> Value
-> Parser ReadmeResponse
forall a. String -> (Object -> Parser a) -> Value -> Parser a
Aeson.withObject String
"ReadmeResponse" ((Object -> Parser ReadmeResponse)
 -> Value -> Parser ReadmeResponse)
-> (Object -> Parser ReadmeResponse)
-> Value
-> Parser ReadmeResponse
forall a b. (a -> b) -> a -> b
$ \Object
o -> do
      Text
markdownReadMe <- Object
o Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
Aeson..: Key
"markdownReadMe"
      ReadmeResponse -> Parser ReadmeResponse
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ReadmeResponse {Text
$sel:markdownReadMe:ReadmeResponse :: Text
markdownReadMe :: Text
markdownReadMe}

-- https://api.unison-lang.org/search?query=%40http
type ShareAPI =
  ("search" :> QueryParam "query" Text :> Get '[JSON] Aeson.Value)
    :<|> ("users" :> Capture "owner-handle" Text :> "projects" :> Capture "project-slug" Text :> "readme" :> Get '[JSON] ReadmeResponse)

httpSearch :: Maybe Text -> Servant.ClientM Aeson.Value
httpProjectReadme :: Text -> Text -> Servant.ClientM ReadmeResponse
( Maybe Text -> ClientM Value
httpSearch
    :<|> Text -> Text -> ClientM ReadmeResponse
httpProjectReadme
  ) =
    let pp :: Proxy ShareAPI
        pp :: Proxy ShareAPI
pp = Proxy ShareAPI
forall {k} (t :: k). Proxy t
Proxy
     in (Proxy ShareAPI -> Client ClientM ShareAPI
forall api.
HasClient ClientM api =>
Proxy api -> Client ClientM api
Servant.client Proxy ShareAPI
pp)

runClientM :: AuthenticatedHttpClient -> Servant.ClientM a -> IO (Either Servant.ClientError a)
runClientM :: forall a.
AuthenticatedHttpClient -> ClientM a -> IO (Either ClientError a)
runClientM (AuthenticatedHttpClient Manager
httpClient) ClientM a
clientM = do
  let clientEnv :: ClientEnv
clientEnv = (Manager -> BaseUrl -> ClientEnv
Servant.mkClientEnv Manager
httpClient (CodeserverURI -> BaseUrl
codeserverBaseURL CodeserverURI
Codeserver.defaultCodeserver))
  ClientM a -> ClientEnv -> IO (Either ClientError a)
forall a. ClientM a -> ClientEnv -> IO (Either ClientError a)
Servant.runClientM ClientM a
clientM ClientEnv
clientEnv