content: {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  {-# LANGUAGE InstanceSigs #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE TypeSynonymInstances #-}
  
  module Backend.Rust.Validator (run, RustValidator (..), RustValidatorEnv (..)) where
  
  import Control.Monad.Reader
  import Convex.Validator
  import System.Directory (createDirectoryIfMissing, doesFileExist)
  import System.Exit (ExitCode (..))
  import System.FilePath ((</>))
  import System.IO (hGetContents)
  import System.Process (CreateProcess (..), StdStream (..), createProcess, proc, waitForProcess)
  
  newtype RustValidator a = RustValidator
    { runRustValidator :: ReaderT RustValidatorEnv IO a
    }
    deriving (Functor, Applicative, Monad, MonadIO, MonadReader RustValidatorEnv, MonadFail)
  
  data RustValidatorEnv = RustValidatorEnv {projectPath :: FilePath}
  
  run :: RustValidatorEnv -> RustValidator a -> IO a
  run renv action = runReaderT (runRustValidator action) renv
  
  instance Validator RustValidator where
    -- \| Sets up the Rust validation sandbox project.
    setup = do
      config <- ask
      let rustProjectPath = projectPath config
      -- Create the directory structure.
      liftIO $ createDirectoryIfMissing True (rustProjectPath </> "src")
  
      -- Write the Cargo.toml file if it doesn't exist.
      let cargoTomlPath = rustProjectPath </> "Cargo.toml"
      cargoTomlExists <- liftIO $ doesFileExist cargoTomlPath
      if not cargoTomlExists
        then liftIO $ writeFile cargoTomlPath cargoTomlContent
        else return ()
  
      -- Write the src/lib.rs file if it doesn't exist.
      let libRsPath = rustProjectPath </> "src" </> "lib.rs"
      libRsExists <- liftIO $ doesFileExist libRsPath
      if not libRsExists
        then liftIO $ writeFile libRsPath libRsContent
        else return ()
  
    -- \| Validates the generated Rust code.
    validate generatedCode = do
      rustProjectPath <- asks projectPath
      let generatedFilePath = rustProjectPath </> "src" </> "generated_api.rs"
  
      -- Write the generated code to the sandbox project.
      liftIO $ writeFile generatedFilePath generatedCode
  
      -- Run `cargo fmt` to format the code.
      liftIO $ putStrLn "[Validator] Running 'cargo fmt'..."
      let fmtCmd = (proc "cargo" ["fmt"]) {cwd = Just rustProjectPath}
      (_, _, _, fmtHandle) <- liftIO $ createProcess fmtCmd
      fmtExitCode <- liftIO $ waitForProcess fmtHandle
  
      if fmtExitCode /= ExitSuccess
        then do
          liftIO $ putStrLn "[Validator] Error: 'cargo fmt' failed. The generated code has syntax errors."
          return Nothing
        else do
          -- Run `cargo check` to validate types and ownership.
          liftIO $ putStrLn "[Validator] Running 'cargo check'..."
          let checkCmd = (proc "cargo" ["check"]) {cwd = Just rustProjectPath, std_err = CreatePipe}
          (_, _, Just stdErr, checkHandle) <- liftIO $ createProcess checkCmd
          checkExitCode <- liftIO $ waitForProcess checkHandle
  
          if checkExitCode == ExitSuccess
            then do
              liftIO $ putStrLn "[Validator] Validation successful."
              -- Read file content from the checked and formatted file.
              content <- liftIO $ readFile generatedFilePath
              return $ Just content
            else do
              liftIO $ putStrLn "[Validator] Error: 'cargo check' failed. The generated code is invalid."
              liftIO $ hGetContents stdErr >>= putStrLn
              return Nothing
  
  -- | The content for the validation project's Cargo.toml file.
  cargoTomlContent :: String
  cargoTomlContent =
    unlines
      [ "[package]",
        "name = \"validation-project\"",
        "version = \"0.1.0\"",
        "edition = \"2021\"",
        "",
        "[dependencies]",
        "convex = \"0.9.0\"",
        "serde = { version = \"1\", features = [\"derive\"] }",
        "serde_json = \"1\"",
        "thiserror = \"1.0\"",
        "anyhow = \"1.0\"",
        "futures-util = { version = \"0.3\" }",
        "tokio = { version = \"1\", features = [\"full\"] }"
      ]
  
  -- | The content for the validation project's src/lib.rs file.
  libRsContent :: String
  libRsContent = "pub mod generated_api;\n"
  