module Stanford.Moss (
MossCfg(..),
defaultMossCfg,
Language(..),
Moss,
liftIO,
withMoss,
addBaseFile,
addFile,
addFilesForStudent,
query
) where
import Control.Exception
import Control.Monad.State
import Data.Monoid
import Network.Simple.TCP
import System.IO
import System.PosixCompat
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
data MossCfg = MossCfg {
mossServer :: HostName,
mossPort :: ServiceName,
mossUser :: BS.ByteString,
mossDir :: Maybe FilePath,
mossX :: Bool,
mossMaxMatches :: Int,
mossShow :: Bool,
mossLanguage :: Language
}
defaultMossCfg :: MossCfg
defaultMossCfg = MossCfg {
mossServer = "moss.stanford.edu",
mossPort = "7690",
mossUser = "",
mossDir = Nothing,
mossX = False,
mossMaxMatches = 250,
mossShow = True,
mossLanguage = Haskell
}
data Language
= C
| CPP
| Java
| CSharp
| Python
| VisualBasic
| Javascript
| FORTRAN
| ML
| Haskell
| Lisp
| Scheme
| Pascal
| Modula2
| Ada
| Perl
| TCL
| Matlab
| VHDL
| Verilog
| Spice
| MIPS
| A8086
| HCL2
deriving (Enum)
instance Show Language where
show C = "c"
show CPP = "cc"
show Java = "java"
show ML = "ml"
show Pascal = "pascal"
show Ada = "ada"
show Lisp = "lisp"
show Scheme = "scheme"
show Haskell = "haskell"
show FORTRAN = "fortran"
data MossSt = MossSt {
mossSocket :: Socket,
mossCounter :: Int,
mossCfg :: MossCfg
}
type Moss = StateT MossSt IO
sendCmd :: Socket -> BS.ByteString -> IO ()
sendCmd s xs = do
putStr "Send: "
putStrLn (show xs)
send s (xs <> "\n")
withMoss :: MossCfg -> Moss a -> IO a
withMoss (cfg@MossCfg {..}) m =
connect mossServer mossPort $ \(s, addr) -> do
sendCmd s ("moss " <> mossUser)
sendCmd s ("X " <> C.pack (show (fromEnum mossX)))
sendCmd s ("maxmatches " <> C.pack (show mossMaxMatches))
sendCmd s ("language " <> C.pack (show mossLanguage))
ls <- recv s 1024
case ls of
Nothing -> error "No data received."
Just "no" -> do
sendCmd s "end"
error "Language not supported"
Just _ -> do
putStrLn "Language supported."
r <- evalStateT m (MossSt s 1 cfg)
sendCmd s "end"
return r
uploadFile :: Int -> String -> FilePath -> Moss ()
uploadFile i dn fp = do
s <- gets mossSocket
MossCfg{..} <- gets mossCfg
liftIO $ do
size <- fileSize <$> getFileStatus fp
sendCmd s ( "file "
<> C.pack (show i) <> " "
<> C.pack (show mossLanguage) <> " "
<> C.pack (show size) <> " "
<> C.pack dn)
xs <- BS.readFile fp
send s xs
addBaseFile :: String -> FilePath -> Moss ()
addBaseFile = uploadFile 0
addFile :: String -> FilePath -> Moss ()
addFile desc fp = do
st <- get
uploadFile (mossCounter st) desc fp
put $ st { mossCounter = mossCounter st + 1 }
addFilesForStudent :: [(String, FilePath)] -> Moss ()
addFilesForStudent fs = do
st <- get
forM_ fs $ \(dn,fn) ->
uploadFile (mossCounter st) dn fn
put $ st { mossCounter = mossCounter st + 1 }
query :: BS.ByteString -> Moss (Maybe BS.ByteString)
query cmt = do
s <- gets mossSocket
liftIO $ do
putStrLn "Querying, this may take several minutes..."
sendCmd s ("query 0 " <> cmt)
recv s 1024