|
| 1 | +{-# LANGUAGE DeriveDataTypeable, ExtendedDefaultRules, OverloadedLists, |
| 2 | + QuasiQuotes, TypeFamilies, TypeSynonymInstances, |
| 3 | + MultiParamTypeClasses #-} |
| 4 | +module Nirum.Targets.Rust ( Rust |
| 5 | + , Code |
| 6 | + , CompileError |
| 7 | + ) where |
| 8 | + |
| 9 | +import qualified Data.Map.Strict as M |
| 10 | +import qualified Data.SemVer as SV |
| 11 | +import qualified Data.Text as T |
| 12 | +import Data.Text.Encoding (encodeUtf8) |
| 13 | +import Data.Text.Lazy (toStrict) |
| 14 | +import Data.Typeable (Typeable) |
| 15 | + |
| 16 | +import GHC.Exts (IsList (toList)) |
| 17 | + |
| 18 | +import System.FilePath (joinPath, replaceExtension) |
| 19 | + |
| 20 | +import Text.Blaze.Renderer.Text |
| 21 | +import Text.Heterocephalus (compileText) |
| 22 | + |
| 23 | +import qualified Nirum.Constructs.Identifier as I |
| 24 | +import Nirum.Constructs.Module |
| 25 | +import Nirum.Constructs.ModulePath (ModulePath) |
| 26 | +import Nirum.Constructs.Name |
| 27 | +import Nirum.Constructs.TypeDeclaration |
| 28 | +import Nirum.Package.Metadata |
| 29 | +import qualified Nirum.Package.ModuleSet as MS |
| 30 | +import Nirum.Targets.Rust.Keyword |
| 31 | +import Nirum.TypeInstance.BoundModule |
| 32 | + |
| 33 | +data Rust = Rust { packageName :: T.Text |
| 34 | + } |
| 35 | + deriving (Eq, Ord, Show, Typeable) |
| 36 | + |
| 37 | +type Code = T.Text |
| 38 | +type CompileError' = () |
| 39 | + |
| 40 | +genCargoToml :: Package Rust -> Code |
| 41 | +genCargoToml Package { metadata = Metadata { version = version' |
| 42 | + , target = Rust { packageName = name' } |
| 43 | + } |
| 44 | + } = |
| 45 | + toStrict $ |
| 46 | + renderMarkup [compileText|[package] |
| 47 | +name = "#{ name' }" |
| 48 | +version = "#{ SV.toLazyText version' }" |
| 49 | +|] |
| 50 | + |
| 51 | +compileModule :: BoundModule Rust -> Code |
| 52 | +compileModule m = |
| 53 | + toStrict $ |
| 54 | + renderMarkup [compileText|%{ forall (moduleName, members') <- enums } |
| 55 | +pub enum #{ toRustIdentifier I.toPascalCaseText $ facialName moduleName } { |
| 56 | +%{ forall EnumMember memberName _ <- members' } |
| 57 | + #{ toRustIdentifier I.toPascalCaseText $ facialName memberName }, |
| 58 | +%{ endforall } |
| 59 | +} |
| 60 | +%{ endforall } |
| 61 | +|] |
| 62 | + where |
| 63 | + moduleTypes :: [TypeDeclaration] |
| 64 | + moduleTypes = toList $ boundTypes m |
| 65 | + enums :: [(Name, [EnumMember])] |
| 66 | + enums = |
| 67 | + [ (moduleName, toList members') |
| 68 | + | TypeDeclaration { typename = moduleName |
| 69 | + , type' = EnumType { members = members' } |
| 70 | + } <- moduleTypes |
| 71 | + ] |
| 72 | + |
| 73 | +compilePackage' :: Package Rust |
| 74 | + -> M.Map FilePath (Either CompileError' Code) |
| 75 | +compilePackage' package = |
| 76 | + M.fromList $ |
| 77 | + [ ( toFilename mp |
| 78 | + , Right $ compileModule m |
| 79 | + ) |
| 80 | + | (mp, _) <- modules' |
| 81 | + , Just m <- [resolveBoundModule mp package] |
| 82 | + ] ++ |
| 83 | + [ ("Cargo.toml", Right $ genCargoToml package) |
| 84 | + , (joinPath ["src", "lib.rs"], Right "") |
| 85 | + ] |
| 86 | + where |
| 87 | + convertModulePath :: ModulePath -> [FilePath] |
| 88 | + convertModulePath mp = |
| 89 | + "src" : |
| 90 | + [ T.unpack (toRustIdentifier I.toSnakeCaseText i) |
| 91 | + | i <- toList mp |
| 92 | + ] |
| 93 | + toFilename :: ModulePath -> FilePath |
| 94 | + toFilename mp = |
| 95 | + replaceExtension (joinPath $ convertModulePath mp) "rs" |
| 96 | + modules' :: [(ModulePath, Module)] |
| 97 | + modules' = MS.toAscList $ modules package |
| 98 | + |
| 99 | +instance Target Rust where |
| 100 | + type CompileResult Rust = Code |
| 101 | + type CompileError Rust = CompileError' |
| 102 | + |
| 103 | + targetName _ = "rust" |
| 104 | + parseTarget table = do |
| 105 | + name' <- stringField "name" table |
| 106 | + return Rust { packageName = name' |
| 107 | + } |
| 108 | + compilePackage = compilePackage' |
| 109 | + showCompileError _ _ = "" |
| 110 | + toByteString _ = encodeUtf8 |
0 commit comments