mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 14:40:17 +00:00
Api for fluence cli (#611)
This commit is contained in:
parent
eb1c8f3f6a
commit
aa89b85b41
39
.github/workflows/release.yml
vendored
39
.github/workflows/release.yml
vendored
@ -40,6 +40,11 @@ jobs:
|
||||
env:
|
||||
BUILD_NUMBER: ${{ github.run_number }}
|
||||
|
||||
- name: JS Aqua API build
|
||||
run: sbt aqua-api/fullLinkJS
|
||||
env:
|
||||
BUILD_NUMBER: ${{ github.run_number }}
|
||||
|
||||
- name: Get project version
|
||||
# In CI sbt appends a new line after its output, so we need `tail -n3 | head -n2` to get last two non-empty lines
|
||||
run: |
|
||||
@ -64,15 +69,22 @@ jobs:
|
||||
|
||||
- name: Check .js exists
|
||||
run: |
|
||||
JS="cli/.js/target/scala-3.1.3/cli-opt/aqua-${{ env.VERSION }}.js"
|
||||
mv cli/.js/target/scala-3.1.3/cli-opt/main.js "$JS"
|
||||
JS="cli/cli/.js/target/scala-3.1.3/cli-opt/aqua-${{ env.VERSION }}.js"
|
||||
mv cli/cli/.js/target/scala-3.1.3/cli-opt/main.js "$JS"
|
||||
stat "$JS"
|
||||
echo "JS=$JS" >> $GITHUB_ENV
|
||||
|
||||
- name: Check API .js exists
|
||||
- name: Check LSP API .js exists
|
||||
run: |
|
||||
JSAPI="language-server-api/target/scala-3.1.3/language-server-api-opt/aqua-${{ env.VERSION }}.js"
|
||||
mv language-server-api/target/scala-3.1.3/language-server-api-opt/main.js "$JSAPI"
|
||||
JSLSP="language-server/language-server-api/target/scala-3.1.3/language-server-api-opt/aqua-${{ env.VERSION }}.js"
|
||||
mv language-server/language-server-api/target/scala-3.1.3/language-server-api-opt/main.js "$JSLSP"
|
||||
stat "$JSLSP"
|
||||
echo "JSLSP=$JSLSP" >> $GITHUB_ENV
|
||||
|
||||
- name: Check Aqua API .js exists
|
||||
run: |
|
||||
JSAPI="api/aqua-api/target/scala-3.1.3/aqua-api-opt/aqua-${{ env.VERSION }}.js"
|
||||
mv api/aqua-api/target/scala-3.1.3/aqua-api-opt/main.js "$JSAPI"
|
||||
stat "$JSAPI"
|
||||
echo "JSAPI=$JSAPI" >> $GITHUB_ENV
|
||||
|
||||
@ -83,7 +95,8 @@ jobs:
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- run: cp ${{ env.JS }} ./npm/aqua.js
|
||||
- run: cp ${{ env.JSAPI }} ./language-server-npm/aqua-lsp-api.js
|
||||
- run: cp ${{ env.JSLSP }} ./language-server-npm/aqua-lsp-api.js
|
||||
- run: cp ${{ env.JSAPI }} ./aqua-api-npm/aqua-api.js
|
||||
|
||||
- run: npm version ${{ env.VERSION }}
|
||||
working-directory: ./npm
|
||||
@ -93,7 +106,7 @@ jobs:
|
||||
npm i
|
||||
npm run build
|
||||
npm publish --access public --tag "${{ github.event.inputs.npm_tag }}"
|
||||
working-directory: ./npm
|
||||
working-directory: ./cli/cli-npm
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
@ -134,13 +147,21 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- run: npm version ${{ env.VERSION }}
|
||||
working-directory: ./language-server-npm
|
||||
working-directory: ./language-server/language-server-npm
|
||||
|
||||
- name: Publish aqua LSP API to NPM
|
||||
run: |
|
||||
npm i
|
||||
npm publish --access public
|
||||
working-directory: ./language-server-npm
|
||||
working-directory: ./language-server/language-server-npm
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish aqua API to NPM
|
||||
run: |
|
||||
npm i
|
||||
npm publish --access public
|
||||
working-directory: ./api/aqua-api-npm
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
|
206
.github/workflows/snapshot.yml
vendored
206
.github/workflows/snapshot.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Publish snapshot
|
||||
name: Publish snapshots
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
@ -14,7 +14,13 @@ on:
|
||||
outputs:
|
||||
aqua-version:
|
||||
description: "@fluencelabs/aqua version"
|
||||
value: ${{ jobs.publish-snapshot.outputs.aqua-version }}
|
||||
value: ${{ jobs.aqua.outputs.version }}
|
||||
aqua-lsp-api-version:
|
||||
description: "@fluencelabs/aqua-language-server-api version"
|
||||
value: ${{ jobs.aqua-lsp-api.outputs.version }}
|
||||
aqua-api-version:
|
||||
description: "@fluencelabs/aqua-api version"
|
||||
value: ${{ jobs.aqua-api.outputs.version }}
|
||||
|
||||
env:
|
||||
FORCE_COLOR: true
|
||||
@ -31,12 +37,9 @@ jobs:
|
||||
repository: fluencelabs/aqua
|
||||
ref: ${{ inputs.ref }}
|
||||
|
||||
- name: Generate version
|
||||
- name: Generate snapshot version
|
||||
id: version
|
||||
run: |
|
||||
SHA=${{ github.event.pull_request.head.sha }}
|
||||
echo "sha=${SHA::7}" >> $GITHUB_OUTPUT
|
||||
echo "branch=${GITHUB_HEAD_REF//[^a-zA-Z0-9-]/-}" >> $GITHUB_OUTPUT
|
||||
uses: fluencelabs/github-actions/generate-snapshot-id@main
|
||||
|
||||
- name: Cache Scala
|
||||
uses: coursier/cache-action@v6
|
||||
@ -44,43 +47,65 @@ jobs:
|
||||
- name: Setup Scala
|
||||
uses: coursier/setup-action@v1
|
||||
|
||||
- name: Compile aqua
|
||||
- name: JS build
|
||||
env:
|
||||
BUILD_NUMBER: ${{ steps.version.outputs.branch }}-${{ steps.version.outputs.sha }}-${{ github.run_number }}-${{ github.run_attempt }}
|
||||
run: sbt "cliJS/fastOptJS"
|
||||
BUILD_NUMBER: ${{ steps.version.outputs.id }}
|
||||
run: sbt cliJS/fastOptJS
|
||||
|
||||
- name: Upload compiled aqua
|
||||
- name: JS LSP API build
|
||||
env:
|
||||
BUILD_NUMBER: ${{ steps.version.outputs.id }}
|
||||
run: sbt language-server-api/fastOptJS
|
||||
|
||||
- name: JS Aqua API build
|
||||
env:
|
||||
BUILD_NUMBER: ${{ steps.version.outputs.id }}
|
||||
run: sbt aqua-api/fastOptJS
|
||||
|
||||
- name: Upload aqua-js artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: aqua
|
||||
path: cli/.js/target/scala-*/cli-fastopt.js
|
||||
name: aqua-js
|
||||
path: cli/cli/.js/target/scala-*/cli-fastopt.js
|
||||
|
||||
publish-snapshot:
|
||||
name: "Publish snapshot"
|
||||
- name: Upload aqua-js-lsp artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: aqua-js-lsp
|
||||
path: language-server/language-server-api/target/scala-*/language-server-api-fastopt.js
|
||||
|
||||
- name: Upload aqua-js-api artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: aqua-js-api
|
||||
path: api/aqua-api/target/scala-*/aqua-api-fastopt.js
|
||||
|
||||
aqua:
|
||||
name: "Publish @fluencelabs/aqua"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs: compile
|
||||
|
||||
outputs:
|
||||
aqua-version: "${{ steps.snapshot.outputs.version }}"
|
||||
version: "${{ steps.snapshot.outputs.version }}"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout aqua
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: fluencelabs/aqua
|
||||
ref: ${{ inputs.ref }}
|
||||
with: ${{ inputs.ref }}
|
||||
|
||||
- name: Download compiled aqua
|
||||
- name: Download aqua-js artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: aqua
|
||||
name: aqua-js
|
||||
|
||||
- run: mv scala-*/cli-fastopt.js npm/aqua.js
|
||||
- run: mv scala-*/cli-fastopt.js cli/cli-npm/aqua.js
|
||||
|
||||
- name: Import secrets
|
||||
uses: hashicorp/vault-action@v2.4.3
|
||||
@ -100,19 +125,19 @@ jobs:
|
||||
with:
|
||||
node-version: "16"
|
||||
registry-url: "https://npm.fluence.dev"
|
||||
cache-dependency-path: "npm/package-lock.json"
|
||||
cache-dependency-path: "cli/cli-npm/package-lock.json"
|
||||
cache: "npm"
|
||||
|
||||
- run: npm i
|
||||
working-directory: npm
|
||||
working-directory: cli/cli-npm
|
||||
|
||||
- name: Set fluence-js version from branch
|
||||
if: inputs.fluence-js-version != 'null'
|
||||
working-directory: npm
|
||||
working-directory: cli/cli-npm
|
||||
run: npm i --save -E @fluencelabs/fluence@${{ inputs.fluence-js-version }}
|
||||
|
||||
- run: npm run build
|
||||
working-directory: npm
|
||||
working-directory: cli/cli-npm
|
||||
|
||||
- name: Generate snapshot version
|
||||
id: version
|
||||
@ -122,5 +147,134 @@ jobs:
|
||||
id: snapshot
|
||||
uses: fluencelabs/github-actions/npm-publish-snapshot@main
|
||||
with:
|
||||
working-directory: npm
|
||||
working-directory: cli/cli-npm
|
||||
id: ${{ steps.version.outputs.id }}
|
||||
|
||||
aqua-lsp:
|
||||
name: "Publish @fluencelabs/aqua-language-server-api"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs: compile
|
||||
|
||||
outputs:
|
||||
version: "${{ steps.snapshot.outputs.version }}"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: fluencelabs/aqua
|
||||
with: ${{ inputs.ref }}
|
||||
|
||||
- name: Download aqua-js-lsp artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: aqua-js-lsp
|
||||
|
||||
- run: mv scala-*/language-server-api-fastopt.js language-server/language-server-npm/aqua-lsp-api.js
|
||||
|
||||
- name: Import secrets
|
||||
uses: hashicorp/vault-action@v2.4.3
|
||||
with:
|
||||
url: https://vault.fluence.dev
|
||||
path: jwt/github
|
||||
role: ci
|
||||
method: jwt
|
||||
jwtGithubAudience: "https://github.com/fluencelabs"
|
||||
jwtTtl: 300
|
||||
exportToken: false
|
||||
secrets: |
|
||||
kv/npm-registry/basicauth/ci token | NODE_AUTH_TOKEN
|
||||
|
||||
- name: Setup node with self-hosted npm registry
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
registry-url: "https://npm.fluence.dev"
|
||||
cache-dependency-path: "language-server/language-server-npm/package-lock.json"
|
||||
cache: "npm"
|
||||
|
||||
- run: npm i
|
||||
working-directory: language-server/language-server-npm
|
||||
|
||||
- name: Generate snapshot version
|
||||
id: version
|
||||
uses: fluencelabs/github-actions/generate-snapshot-id@main
|
||||
|
||||
- name: Publish snapshot
|
||||
id: snapshot
|
||||
uses: fluencelabs/github-actions/npm-publish-snapshot@main
|
||||
with:
|
||||
working-directory: language-server/language-server-npm
|
||||
id: ${{ steps.version.outputs.id }}
|
||||
|
||||
aqua-api:
|
||||
name: "Publish @fluencelabs/aqua-api"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs: compile
|
||||
|
||||
outputs:
|
||||
version: "${{ steps.snapshot.outputs.version }}"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: fluencelabs/aqua
|
||||
with: ${{ inputs.ref }}
|
||||
|
||||
- name: Download aqua-js artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: aqua-js-api
|
||||
|
||||
- run: mv scala-*/aqua-api-fastopt.js api/aqua-api-npm/aqua-api.js
|
||||
|
||||
- name: Import secrets
|
||||
uses: hashicorp/vault-action@v2.4.3
|
||||
with:
|
||||
url: https://vault.fluence.dev
|
||||
path: jwt/github
|
||||
role: ci
|
||||
method: jwt
|
||||
jwtGithubAudience: "https://github.com/fluencelabs"
|
||||
jwtTtl: 300
|
||||
exportToken: false
|
||||
secrets: |
|
||||
kv/npm-registry/basicauth/ci token | NODE_AUTH_TOKEN
|
||||
|
||||
- name: Setup node with self-hosted npm registry
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
registry-url: "https://npm.fluence.dev"
|
||||
cache-dependency-path: "api/aqua-api-npm/package-lock.json"
|
||||
cache: "npm"
|
||||
|
||||
- run: npm i
|
||||
working-directory: api/aqua-api-npm
|
||||
|
||||
- name: Set fluence-js version from branch
|
||||
if: inputs.fluence-js-version != 'null'
|
||||
working-directory: cli/cli-npm
|
||||
run: npm i --save-dev -E @fluencelabs/fluence@${{ inputs.fluence-js-version }}
|
||||
|
||||
- name: Generate snapshot version
|
||||
id: version
|
||||
uses: fluencelabs/github-actions/generate-snapshot-id@main
|
||||
|
||||
- name: Publish snapshot
|
||||
id: snapshot
|
||||
uses: fluencelabs/github-actions/npm-publish-snapshot@main
|
||||
with:
|
||||
working-directory: api/aqua-api-npm
|
||||
id: ${{ steps.version.outputs.id }}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,3 +10,4 @@ project/target
|
||||
.DS_Store
|
||||
|
||||
npm/aqua.js
|
||||
**/node_modules
|
||||
|
27
api/aqua-api-npm/aqua-api.d.ts
vendored
Normal file
27
api/aqua-api-npm/aqua-api.d.ts
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
import type {FunctionCallDef, ServiceDef} from "@fluencelabs/fluence/dist/internal/compilerSupport/v3impl/interface"
|
||||
|
||||
export class AquaConfig {
|
||||
constructor(logLevel: string, constants: string[], noXor: boolean, noRelay: boolean);
|
||||
logLevel?: string
|
||||
constants?: string[]
|
||||
noXor?: boolean
|
||||
noRelay?: boolean
|
||||
}
|
||||
|
||||
export class AquaFunction {
|
||||
funcDef: FunctionCallDef
|
||||
script: string
|
||||
}
|
||||
|
||||
export class CompilationResult {
|
||||
services: ServiceDef[]
|
||||
functions: Record<string, AquaFunction>
|
||||
}
|
||||
|
||||
export class Compiler {
|
||||
compileRun(functionStr: string, arguments: any, path: string, imports: string[], config?: AquaConfig): Promise<AquaFunction>;
|
||||
compile(path: string, imports: string[], config?: AquaConfig): Promise<CompilationResult>;
|
||||
compileString(input: string, imports: string[], config?: AquaConfig): Promise<CompilationResult>;
|
||||
}
|
||||
|
||||
export var Aqua: Compiler;
|
1
api/aqua-api-npm/meta-utils.js
Normal file
1
api/aqua-api-npm/meta-utils.js
Normal file
@ -0,0 +1 @@
|
||||
const metaUrl = ""
|
12982
api/aqua-api-npm/package-lock.json
generated
Normal file
12982
api/aqua-api-npm/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
api/aqua-api-npm/package.json
Normal file
32
api/aqua-api-npm/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@fluencelabs/aqua-api",
|
||||
"version": "0.0.3",
|
||||
"description": "Aqua API",
|
||||
"type": "commonjs",
|
||||
"files": [
|
||||
"aqua-api.js",
|
||||
"aqua-api.d.ts",
|
||||
"meta-utils.js"
|
||||
],
|
||||
"scripts": {
|
||||
"move:scalajs": "cp ../aqua-api/target/scala-3.1.3/aqua-api-opt/main.js ./aqua-api.js",
|
||||
"move:fast": "cp ../aqua-api/target/scala-3.1.3/aqua-api-fastopt/main.js ./aqua-api.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fluencelabs/aqua.git"
|
||||
},
|
||||
"keywords": [
|
||||
"aqua",
|
||||
"fluence"
|
||||
],
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fluencelabs/aqua/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fluencelabs/aqua#readme",
|
||||
"devDependencies": {
|
||||
"@fluencelabs/fluence": "^0.27.3"
|
||||
}
|
||||
}
|
237
api/aqua-api/src/main/scala/aqua/api/AquaAPI.scala
Normal file
237
api/aqua-api/src/main/scala/aqua/api/AquaAPI.scala
Normal file
@ -0,0 +1,237 @@
|
||||
package aqua.api
|
||||
|
||||
import aqua.ErrorRendering.showError
|
||||
import aqua.backend.{AirFunction, Backend, Generated}
|
||||
import aqua.compiler.*
|
||||
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
|
||||
import aqua.logging.{LogFormatter, LogLevels}
|
||||
import aqua.constants.Constants
|
||||
import aqua.io.*
|
||||
import aqua.raw.ops.Call
|
||||
import aqua.run.{CallInfo, CallPreparer, CliFunc, FuncCompiler, RunPreparer}
|
||||
import aqua.parser.lexer.{LiteralToken, Token}
|
||||
import aqua.parser.lift.FileSpan.F
|
||||
import aqua.parser.lift.{FileSpan, Span}
|
||||
import aqua.parser.{ArrowReturnError, BlockIndentError, LexerError, ParserError}
|
||||
import aqua.semantics.{CompilerState, HeaderError, RulesViolated, WrongAST}
|
||||
import aqua.{AquaIO, SpanParser}
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.backend.api.APIBackend
|
||||
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
|
||||
import cats.data.Validated.{invalidNec, validNec, Invalid, Valid}
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.effect.IO
|
||||
import cats.effect.unsafe.implicits.global
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSConverters.*
|
||||
import scala.scalajs.js.annotation.*
|
||||
import scala.scalajs.js.{undefined, UndefOr}
|
||||
import aqua.js.{FunctionDefJs, ServiceDefJs, VarJson}
|
||||
import aqua.model.AquaContext
|
||||
import aqua.raw.ops.CallArrowRawTag
|
||||
import aqua.raw.value.{LiteralRaw, VarRaw}
|
||||
import aqua.res.AquaRes
|
||||
import cats.Applicative
|
||||
|
||||
@JSExportTopLevel("AquaFunction")
|
||||
case class AquaFunction(
|
||||
@JSExport
|
||||
funcDef: FunctionDefJs,
|
||||
@JSExport
|
||||
script: String
|
||||
)
|
||||
|
||||
case class AquaAPIConfig(
|
||||
logLevel: String = "info",
|
||||
constants: List[String] = Nil,
|
||||
noXor: Boolean = false,
|
||||
noRelay: Boolean = false
|
||||
)
|
||||
|
||||
object AquaAPIConfig {
|
||||
|
||||
def fromJS(cjs: AquaConfig): AquaAPIConfig = {
|
||||
AquaAPIConfig(
|
||||
cjs.logLevel.getOrElse("info"),
|
||||
cjs.constants.map(_.toList).getOrElse(Nil),
|
||||
cjs.noXor.getOrElse(false),
|
||||
cjs.noRelay.getOrElse(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@JSExportTopLevel("AquaConfig")
|
||||
case class AquaConfig(
|
||||
@JSExport
|
||||
logLevel: js.UndefOr[String],
|
||||
@JSExport
|
||||
constants: js.UndefOr[js.Array[String]],
|
||||
@JSExport
|
||||
noXor: js.UndefOr[Boolean],
|
||||
@JSExport
|
||||
noRelay: js.UndefOr[Boolean]
|
||||
)
|
||||
|
||||
@JSExportTopLevel("CompilationResult")
|
||||
case class CompilationResult(
|
||||
@JSExport
|
||||
services: js.Array[ServiceDefJs],
|
||||
@JSExport
|
||||
functions: js.Dictionary[AquaFunction]
|
||||
)
|
||||
|
||||
@JSExportTopLevel("Aqua")
|
||||
object AquaAPI extends App with Logging {
|
||||
|
||||
def getTag(serviceId: String, value: VarRaw) = {
|
||||
CallArrowRawTag.service(
|
||||
LiteralRaw.quote(serviceId),
|
||||
value.name,
|
||||
Call(List.empty, List(Call.Export(value.name, value.baseType)))
|
||||
)
|
||||
}
|
||||
|
||||
@JSExport
|
||||
def compileRun(
|
||||
functionStr: String,
|
||||
arguments: js.Dynamic,
|
||||
pathStr: String,
|
||||
imports: js.Array[String],
|
||||
aquaConfigJS: js.UndefOr[AquaConfig]
|
||||
): js.Promise[AquaFunction] = {
|
||||
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
|
||||
val aquaConfig: AquaAPIConfig =
|
||||
aquaConfigJS.toOption.map(cjs => AquaAPIConfig.fromJS(cjs)).getOrElse(AquaAPIConfig())
|
||||
LogFormatter.initLogger(Some(LogLevels.levelFromString(aquaConfig.logLevel).toOption.get))
|
||||
val transformConfig = TransformConfig()
|
||||
|
||||
new FuncCompiler[IO](
|
||||
Some(RelativePath(Path(pathStr))),
|
||||
imports.toList.map(Path.apply),
|
||||
transformConfig
|
||||
).compile().map { contextV =>
|
||||
contextV.andThen { context =>
|
||||
CliFunc.fromString(functionStr).leftMap(errs => NonEmptyChain.fromNonEmptyList(errs)).andThen { cliFunc =>
|
||||
FuncCompiler.findFunction(context, cliFunc).andThen { arrow =>
|
||||
VarJson.checkDataGetServices(cliFunc.args, Some(arguments)).andThen {
|
||||
case (argsWithTypes, _) =>
|
||||
val func = cliFunc.copy(args = argsWithTypes)
|
||||
val preparer = new RunPreparer(
|
||||
func,
|
||||
arrow,
|
||||
transformConfig
|
||||
)
|
||||
preparer.prepare().map { ci =>
|
||||
AquaFunction(FunctionDefJs(ci.definitions), ci.air)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.flatMap {
|
||||
case Valid(result) => IO.pure(result)
|
||||
case Invalid(err) =>
|
||||
err.map(_.show).distinct.map(OutputPrinter.errorF[IO]).sequence
|
||||
IO.raiseError[AquaFunction](new Error("Compilation failed."))
|
||||
}.unsafeToFuture().toJSPromise
|
||||
|
||||
}
|
||||
|
||||
@JSExport
|
||||
def compile(
|
||||
pathStr: String,
|
||||
imports: js.Array[String],
|
||||
aquaConfigJS: js.UndefOr[AquaConfig]
|
||||
): js.Promise[CompilationResult] = {
|
||||
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
|
||||
val path = Path(pathStr)
|
||||
val sources = new AquaFileSources[IO](path, imports.toList.map(Path.apply))
|
||||
compileRaw(aquaConfigJS, sources)
|
||||
}
|
||||
|
||||
@JSExport
|
||||
def compileString(
|
||||
input: String,
|
||||
imports: js.Array[String],
|
||||
aquaConfigJS: js.UndefOr[AquaConfig]
|
||||
): js.Promise[CompilationResult] = {
|
||||
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
|
||||
val path = Path("")
|
||||
val strSources: AquaFileSources[IO] =
|
||||
new AquaFileSources[IO](path, imports.toList.map(Path.apply)) {
|
||||
override def sources: IO[ValidatedNec[AquaFileError, Chain[(FileModuleId, String)]]] = {
|
||||
IO.pure(Valid(Chain.one((FileModuleId(path), input))))
|
||||
}
|
||||
}
|
||||
compileRaw(aquaConfigJS, strSources)
|
||||
}
|
||||
|
||||
def compileRaw(
|
||||
aquaConfigJS: js.UndefOr[AquaConfig],
|
||||
sources: AquaSources[IO, AquaFileError, FileModuleId]
|
||||
): js.Promise[CompilationResult] = {
|
||||
val aquaConfig =
|
||||
aquaConfigJS.toOption.map(cjs => AquaAPIConfig.fromJS(cjs)).getOrElse(AquaAPIConfig())
|
||||
|
||||
(
|
||||
LogLevels.levelFromString(aquaConfig.logLevel),
|
||||
Constants.parse(aquaConfig.constants)
|
||||
).mapN { (level, constants) =>
|
||||
|
||||
LogFormatter.initLogger(Some(level))
|
||||
|
||||
val config = AquaCompilerConf(constants)
|
||||
val transformConfig = TransformConfig()
|
||||
|
||||
val proc = for {
|
||||
res <- CompilerAPI
|
||||
.compile[IO, AquaFileError, FileModuleId, FileSpan.F](
|
||||
sources,
|
||||
SpanParser.parser,
|
||||
new AirValidator[IO] {
|
||||
override def init(): IO[Unit] = Applicative[IO].pure(())
|
||||
override def validate(airs: List[AirFunction]): IO[ValidatedNec[String, Unit]] =
|
||||
Applicative[IO].pure(validNec(()))
|
||||
},
|
||||
new Backend.Transform:
|
||||
override def transform(ex: AquaContext): AquaRes =
|
||||
Transform.contextRes(ex, transformConfig)
|
||||
|
||||
override def generate(aqua: AquaRes): Seq[Generated] = APIBackend.generate(aqua)
|
||||
,
|
||||
config
|
||||
)
|
||||
jsResult <- res match {
|
||||
case Valid(compiled) =>
|
||||
val allGenerated: List[Generated] = compiled.toList.flatMap(_.compiled)
|
||||
val serviceDefs = allGenerated.flatMap(_.services).map(s => ServiceDefJs(s)).toJSArray
|
||||
val functions = allGenerated.flatMap(
|
||||
_.air.map(as => (as.name, AquaFunction(FunctionDefJs(as.funcDef), as.air)))
|
||||
)
|
||||
|
||||
IO.pure(CompilationResult(serviceDefs, js.Dictionary.apply(functions: _*)))
|
||||
case Invalid(errChain) =>
|
||||
errChain.map(_.show).distinct.map(OutputPrinter.errorF[IO]).sequence
|
||||
IO.raiseError[CompilationResult](new Error("Compilation failed."))
|
||||
}
|
||||
} yield {
|
||||
jsResult
|
||||
}
|
||||
|
||||
proc.unsafeToFuture().toJSPromise
|
||||
} match {
|
||||
case Valid(pr) => pr
|
||||
case Invalid(err) => js.Promise.reject(err)
|
||||
}
|
||||
}
|
||||
}
|
15
aqua-run/src/main/scala/aqua/run/CallInfo.scala
Normal file
15
aqua-run/src/main/scala/aqua/run/CallInfo.scala
Normal file
@ -0,0 +1,15 @@
|
||||
package aqua.run
|
||||
import aqua.definitions.FunctionDef
|
||||
|
||||
case class CallInfo(
|
||||
name: String,
|
||||
air: String,
|
||||
definitions: FunctionDef,
|
||||
config: RunConfig
|
||||
)
|
||||
|
||||
case class RunInfo(
|
||||
name: String,
|
||||
air: String,
|
||||
definitions: FunctionDef
|
||||
)
|
142
aqua-run/src/main/scala/aqua/run/CallPreparer.scala
Normal file
142
aqua-run/src/main/scala/aqua/run/CallPreparer.scala
Normal file
@ -0,0 +1,142 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.backend.air.FuncAirGen
|
||||
import aqua.definitions.{FunctionDef, TypeDefinition}
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{FuncArrow, ValueModel, VarModel}
|
||||
import aqua.parser.lexer.CallArrowToken
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, SeqTag}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNec}
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.partialOrder.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Id, ~>}
|
||||
|
||||
import scala.collection.immutable.SortedMap
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
class CallPreparer(
|
||||
func: CliFunc,
|
||||
funcCallable: FuncArrow,
|
||||
getters: List[CallArrowRawTag],
|
||||
printResultTag: List[VarRaw] => CallArrowRawTag,
|
||||
finisherService: CallArrowRawTag,
|
||||
config: RunConfig,
|
||||
transformConfig: TransformConfig
|
||||
) {
|
||||
|
||||
def validateArguments(
|
||||
funcDomain: List[(String, Type)],
|
||||
args: List[ValueRaw]
|
||||
): ValidatedNec[String, Unit] = {
|
||||
if (funcDomain.size != args.length) {
|
||||
invalidNec(
|
||||
s"Number of arguments for the function is incorrect. Expected: ${args.length}. Actual: ${funcDomain.size}"
|
||||
)
|
||||
} else {
|
||||
funcDomain
|
||||
.zip(args)
|
||||
.map { case ((name, lt), rt) =>
|
||||
rt match {
|
||||
case VarRaw(n, _) =>
|
||||
TypeValidator.validateTypes(n, lt, Some(rt.`type`))
|
||||
case _ =>
|
||||
TypeValidator.validateTypes(name, lt, Some(rt.`type`))
|
||||
}
|
||||
|
||||
}
|
||||
.sequence
|
||||
.map(_ => ())
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps function with necessary services, registers services and calls wrapped function with FluenceJS
|
||||
def prepare(): ValidatedNec[String, CallInfo] = {
|
||||
validateArguments(
|
||||
funcCallable.arrowType.domain.labelledData,
|
||||
func.args
|
||||
).map(_ =>
|
||||
genCallInfo(
|
||||
wrapCall()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Generates air from function, register all services and make a call through FluenceJS
|
||||
private def genCallInfo(
|
||||
wrapped: FuncArrow
|
||||
): CallInfo = {
|
||||
// TODO: prob we can turn this Eval into F
|
||||
val funcRes = Transform.funcRes(wrapped, transformConfig).value
|
||||
val definitions = FunctionDef(funcRes)
|
||||
|
||||
val air = FuncAirGen(funcRes).generate.show
|
||||
|
||||
if (config.common.flags.printAir) {
|
||||
OutputPrinter.print(air)
|
||||
}
|
||||
|
||||
CallInfo(func.name, air, definitions, config)
|
||||
}
|
||||
|
||||
// Wrap a function like this:
|
||||
// func wrapFunc():
|
||||
// arg1 <- getDataSrv()
|
||||
// arg2 <- getDataSrv()
|
||||
// ...
|
||||
// res <- funcCallable(args:_*)
|
||||
// Console.print(res)
|
||||
// Finisher.finish()
|
||||
private def wrapCall(): FuncArrow = {
|
||||
val codomain = funcCallable.arrowType.codomain.toList
|
||||
// pass results to a printing service if an input function returns a result
|
||||
// otherwise just call it
|
||||
val body = codomain match {
|
||||
case Nil =>
|
||||
CallArrowRawTag.func(func.name, Call(func.args, Nil)).leaf
|
||||
case types =>
|
||||
val (variables, exports) = types.zipWithIndex.map { case (t, idx) =>
|
||||
val name = config.resultName + idx
|
||||
(VarRaw(name, t), Call.Export(name, t))
|
||||
}.unzip
|
||||
val callFuncTag =
|
||||
CallArrowRawTag.func(func.name, Call(func.args, exports))
|
||||
|
||||
val consoleServiceTag = printResultTag(variables)
|
||||
|
||||
SeqTag.wrap(
|
||||
callFuncTag.leaf,
|
||||
consoleServiceTag.leaf
|
||||
)
|
||||
}
|
||||
|
||||
// return something to wait a result if we have return value in function
|
||||
// this is needed to catch an error if it will be occurred
|
||||
val (returnCodomain, ret) = if (codomain.isEmpty) {
|
||||
(NilType, Nil)
|
||||
} else {
|
||||
(UnlabeledConsType(ScalarType.string, NilType), LiteralRaw.quote("ok") :: Nil)
|
||||
}
|
||||
|
||||
FuncArrow(
|
||||
config.functionWrapperName,
|
||||
SeqTag.wrap((getters.map(_.leaf) :+ body :+ finisherService.leaf): _*),
|
||||
// no arguments and returns "ok" string
|
||||
ArrowType(NilType, returnCodomain),
|
||||
ret,
|
||||
Map(func.name -> funcCallable),
|
||||
Map.empty,
|
||||
None
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
71
aqua-run/src/main/scala/aqua/run/CliFunc.scala
Normal file
71
aqua-run/src/main/scala/aqua/run/CliFunc.scala
Normal file
@ -0,0 +1,71 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.parser.lexer.{CallArrowToken, CollectionToken, LiteralToken, VarToken}
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.raw.value.{CollectionRaw, LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.{ArrayType, BottomType}
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNel}
|
||||
import cats.data.Validated.{invalid, invalidNel, validNel}
|
||||
import cats.{Id, ~>}
|
||||
import cats.syntax.traverse.*
|
||||
|
||||
case class CliFunc(name: String, args: List[ValueRaw] = Nil, ability: Option[String] = None)
|
||||
|
||||
object CliFunc {
|
||||
|
||||
def spanToId: Span.S ~> Id = new (Span.S ~> Id) {
|
||||
|
||||
override def apply[A](span: Span.S[A]): Id[A] = {
|
||||
span._2
|
||||
}
|
||||
}
|
||||
|
||||
def fromString(func: String): ValidatedNel[String, CliFunc] = {
|
||||
CallArrowToken.callArrow.parseAll(func.trim) match {
|
||||
case Right(exprSpan) =>
|
||||
val expr = exprSpan.mapK(spanToId)
|
||||
|
||||
val argsV = expr.args.collect {
|
||||
case LiteralToken(value, ts) =>
|
||||
validNel(LiteralRaw(value, ts))
|
||||
case VarToken(name, _) =>
|
||||
validNel(VarRaw(name.value, BottomType))
|
||||
case CollectionToken(_, values) =>
|
||||
val hasVariables = values.exists {
|
||||
case LiteralToken(_, _) => false
|
||||
case _ => true
|
||||
}
|
||||
if (!hasVariables) {
|
||||
val literals = values.collect { case LiteralToken(value, ts) =>
|
||||
LiteralRaw(value, ts)
|
||||
}
|
||||
val hasSameTypesOrEmpty =
|
||||
literals.isEmpty || literals.map(_.baseType).toSet.size == 1
|
||||
|
||||
if (hasSameTypesOrEmpty) {
|
||||
validNel(
|
||||
NonEmptyList
|
||||
.fromList(literals)
|
||||
.map(l => CollectionRaw(l, ArrayType(l.head.baseType)))
|
||||
.getOrElse(ValueRaw.Nil)
|
||||
)
|
||||
} else
|
||||
invalidNel(
|
||||
"If the argument is an array, then it must contain elements of the same type."
|
||||
)
|
||||
|
||||
} else
|
||||
invalidNel(
|
||||
"Array arguments can only have numbers, strings, or booleans."
|
||||
)
|
||||
case CallArrowToken(_, _, _) =>
|
||||
invalidNel("Function calls as arguments are not supported.")
|
||||
}.sequence
|
||||
argsV.andThen(args =>
|
||||
validNel(CliFunc(expr.funcName.value, args, expr.ability.map(_.name)))
|
||||
)
|
||||
|
||||
case Left(err) => invalid(err.expected.map(_.context.mkString("\n")))
|
||||
}
|
||||
}
|
||||
}
|
99
aqua-run/src/main/scala/aqua/run/FuncCompiler.scala
Normal file
99
aqua-run/src/main/scala/aqua/run/FuncCompiler.scala
Normal file
@ -0,0 +1,99 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.ErrorRendering.showError
|
||||
import aqua.compiler.{AquaCompiler, AquaCompilerConf, CompilerAPI}
|
||||
import aqua.files.{AquaFileSources, FileModuleId}
|
||||
import aqua.{AquaIO, SpanParser}
|
||||
import aqua.io.{AquaFileError, AquaPath, PackagePath, Prelude}
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.model.{AquaContext, FuncArrow}
|
||||
import aqua.parser.lift.FileSpan
|
||||
import aqua.run.CliFunc
|
||||
import cats.data.Validated.{invalidNec, validNec}
|
||||
import cats.data.{Chain, NonEmptyList, Validated, ValidatedNec}
|
||||
import cats.effect.IO
|
||||
import cats.effect.kernel.{Async, Clock}
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.monad.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
class FuncCompiler[F[_]: Files: AquaIO: Async](
|
||||
input: Option[AquaPath],
|
||||
imports: List[Path],
|
||||
transformConfig: TransformConfig
|
||||
) extends Logging {
|
||||
|
||||
private def compileToContext(
|
||||
path: Path,
|
||||
imports: List[Path],
|
||||
config: AquaCompilerConf = AquaCompilerConf(transformConfig.constantsList)
|
||||
) = {
|
||||
val sources = new AquaFileSources[F](path, imports)
|
||||
CompilerAPI
|
||||
.compileToContext[F, AquaFileError, FileModuleId, FileSpan.F](
|
||||
sources,
|
||||
SpanParser.parser,
|
||||
config
|
||||
)
|
||||
.map(_.leftMap(_.map(_.show)))
|
||||
}
|
||||
|
||||
private def compileBuiltins() = {
|
||||
for {
|
||||
path <- PackagePath.builtin.getPath()
|
||||
context <- compileToContext(path, Nil)
|
||||
} yield {
|
||||
context
|
||||
}
|
||||
}
|
||||
|
||||
// Compile and get only one function
|
||||
def compile(
|
||||
preludeImports: List[Path] = Nil,
|
||||
withBuiltins: Boolean = false
|
||||
): F[ValidatedNec[String, Chain[AquaContext]]] = {
|
||||
for {
|
||||
// compile builtins and add it to context
|
||||
builtinsV <-
|
||||
if (withBuiltins) compileBuiltins()
|
||||
else validNec[String, Chain[AquaContext]](Chain.empty).pure[F]
|
||||
compileResult <- input.map { ap =>
|
||||
// compile only context to wrap and call function later
|
||||
Clock[F].timed(
|
||||
ap.getPath().flatMap(p => compileToContext(p, preludeImports ++ imports))
|
||||
)
|
||||
}.getOrElse((Duration.Zero, validNec[String, Chain[AquaContext]](Chain.empty)).pure[F])
|
||||
(compileTime, contextV) = compileResult
|
||||
} yield {
|
||||
logger.debug(s"Compile time: ${compileTime.toMillis}ms")
|
||||
// add builtins to the end of context
|
||||
contextV.andThen(c => builtinsV.map(bc => c ++ bc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object FuncCompiler {
|
||||
|
||||
def findFunction(
|
||||
contexts: Chain[AquaContext],
|
||||
func: CliFunc
|
||||
): ValidatedNec[String, FuncArrow] =
|
||||
func.ability
|
||||
.fold(
|
||||
contexts
|
||||
.collectFirstSome(_.allFuncs.get(func.name))
|
||||
)(ab => contexts.collectFirstSome(_.abilities.get(ab).flatMap(_.allFuncs.get(func.name))))
|
||||
.map(validNec)
|
||||
.getOrElse(
|
||||
Validated.invalidNec[String, FuncArrow](
|
||||
s"There is no function '${func.ability.map(_ + ".").getOrElse("")}${func.name}' or it is not exported. Check the spelling or see https://fluence.dev/docs/aqua-book/language/header/#export"
|
||||
)
|
||||
)
|
||||
}
|
39
aqua-run/src/main/scala/aqua/run/RunConfig.scala
Normal file
39
aqua-run/src/main/scala/aqua/run/RunConfig.scala
Normal file
@ -0,0 +1,39 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.logging.LogLevels
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.raw.value.VarRaw
|
||||
import scribe.Level
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.util.Try
|
||||
|
||||
case class Flags(
|
||||
printAir: Boolean,
|
||||
showConfig: Boolean,
|
||||
verbose: Boolean,
|
||||
noXor: Boolean,
|
||||
noRelay: Boolean
|
||||
)
|
||||
|
||||
case class GeneralOptions(
|
||||
timeout: Duration,
|
||||
logLevel: LogLevels,
|
||||
multiaddr: String,
|
||||
on: Option[String],
|
||||
flags: Flags,
|
||||
secretKey: Option[Array[Byte]],
|
||||
constants: List[ConstantRaw]
|
||||
)
|
||||
|
||||
// `run` command configuration
|
||||
case class RunConfig(
|
||||
common: GeneralOptions,
|
||||
resultPrinterServiceId: String = "--after-callback-srv-service--",
|
||||
resultPrinterName: String = "console-log",
|
||||
finisherServiceId: String = "--finisher--",
|
||||
finisherFnName: String = "--finish-execution--",
|
||||
resultName: String = "-some-unique-res-name-",
|
||||
functionWrapperName: String = "--someFuncToRun--"
|
||||
)
|
120
aqua-run/src/main/scala/aqua/run/RunPreparer.scala
Normal file
120
aqua-run/src/main/scala/aqua/run/RunPreparer.scala
Normal file
@ -0,0 +1,120 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.backend.air.FuncAirGen
|
||||
import aqua.definitions.{FunctionDef, TypeDefinition}
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{FuncArrow, ValueModel, VarModel}
|
||||
import aqua.parser.lexer.CallArrowToken
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, SeqTag}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNec}
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.partialOrder.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{~>, Id}
|
||||
|
||||
import scala.collection.immutable.SortedMap
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
class RunPreparer(
|
||||
func: CliFunc,
|
||||
funcCallable: FuncArrow,
|
||||
transformConfig: TransformConfig
|
||||
) {
|
||||
|
||||
def validateArguments(
|
||||
funcDomain: List[(String, Type)],
|
||||
args: List[ValueRaw]
|
||||
): ValidatedNec[String, Unit] = {
|
||||
if (funcDomain.size != args.length) {
|
||||
invalidNec(
|
||||
s"Number of arguments for the function is incorrect. Expected: ${args.length}. Actual: ${funcDomain.size}"
|
||||
)
|
||||
} else {
|
||||
funcDomain
|
||||
.zip(args)
|
||||
.map { case ((name, lt), rt) =>
|
||||
rt match {
|
||||
case VarRaw(n, _) =>
|
||||
TypeValidator.validateTypes(n, lt, Some(rt.`type`))
|
||||
case _ =>
|
||||
TypeValidator.validateTypes(name, lt, Some(rt.`type`))
|
||||
}
|
||||
|
||||
}
|
||||
.sequence
|
||||
.map(_ => ())
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps function with necessary services, registers services and calls wrapped function with FluenceJS
|
||||
def prepare(): ValidatedNec[String, RunInfo] = {
|
||||
validateArguments(
|
||||
funcCallable.arrowType.domain.labelledData,
|
||||
func.args
|
||||
).map(_ =>
|
||||
genCallInfo(
|
||||
wrapCall()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Generates air from function, register all services and make a call through FluenceJS
|
||||
private def genCallInfo(
|
||||
wrapped: FuncArrow
|
||||
): RunInfo = {
|
||||
// TODO: prob we can turn this Eval into F
|
||||
val funcRes = Transform.funcRes(wrapped, transformConfig).value
|
||||
val definitions = FunctionDef(funcRes)
|
||||
|
||||
val air = FuncAirGen(funcRes).generate.show
|
||||
|
||||
RunInfo(func.name, air, definitions)
|
||||
}
|
||||
|
||||
private def wrapCall(): FuncArrow = {
|
||||
val codomain = funcCallable.arrowType.codomain.toList
|
||||
// pass results to a printing service if an input function returns a result
|
||||
// otherwise just call it
|
||||
val (results, body) = codomain match {
|
||||
case Nil =>
|
||||
Nil -> CallArrowRawTag.func(func.name, Call(func.args, Nil)).leaf
|
||||
case types =>
|
||||
val (variables, exports) = types.zipWithIndex.map { case (t, idx) =>
|
||||
val name = func.name + "_result" + idx
|
||||
(VarRaw(name, t), Call.Export(name, t))
|
||||
}.unzip
|
||||
|
||||
val callFuncTag =
|
||||
CallArrowRawTag.func(func.name, Call(func.args, exports))
|
||||
|
||||
variables -> callFuncTag.leaf
|
||||
}
|
||||
|
||||
val returnCodomain = ProductType(results.map(_.`type`))
|
||||
|
||||
// arguments is only variables, without literals
|
||||
val argumentsType = ProductType.labelled(func.args.zip(funcCallable.arrowType.domain.labelledData).collect {
|
||||
case (VarRaw(name, _), (_, t)) => (name, t)
|
||||
})
|
||||
|
||||
FuncArrow(
|
||||
func.name + "Run",
|
||||
SeqTag.wrap(body),
|
||||
ArrowType(argumentsType, returnCodomain),
|
||||
results,
|
||||
Map(func.name -> funcCallable),
|
||||
Map.empty,
|
||||
None
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package aqua.json
|
||||
package aqua.run
|
||||
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
@ -13,11 +13,11 @@ import cats.syntax.traverse.*
|
||||
|
||||
import scala.collection.immutable.SortedMap
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSON
|
||||
|
||||
object TypeValidator {
|
||||
|
||||
import aqua.types.Type.typesPartialOrder
|
||||
|
||||
/**
|
||||
* Compare and validate type from Aqua file and type generated from JSON.
|
||||
* Also, the validation will succeed if the JSON type is missing an array or an optional field.
|
@ -1,3 +1,7 @@
|
||||
aqua FooBars declares wait
|
||||
|
||||
export wait
|
||||
|
||||
data Record:
|
||||
relay_id: []string
|
||||
peer_id: string
|
||||
@ -70,9 +74,25 @@ service Ser("ser"):
|
||||
-- Op2.identity(res!2)
|
||||
-- <- res
|
||||
|
||||
func streamAssignment(arr: []string) -> string:
|
||||
stream: *[]u32
|
||||
stream <<- [0]
|
||||
a = stream[arr.length - 1][0]
|
||||
b = arr[a]
|
||||
<- b
|
||||
data InnerObj:
|
||||
arr: []string
|
||||
num: u32
|
||||
|
||||
data SomeObj:
|
||||
str: string
|
||||
num: u64
|
||||
inner: InnerObj
|
||||
|
||||
func wait(i: []u32) -> SomeObj:
|
||||
<- SomeObj(str = "some str",
|
||||
num = 4,
|
||||
inner = InnerObj(arr = ["a", "b", "c"], num = i[2])
|
||||
)
|
||||
|
||||
-- func a(nums: []u32) -> []u32:
|
||||
-- <- nums
|
||||
--
|
||||
-- func some():
|
||||
-- a([1,2,3,4])
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package aqua.backend.air
|
||||
|
||||
import aqua.backend.{AirString, Backend, Generated, Version}
|
||||
import aqua.backend.{AirFunction, Backend, Generated, Version}
|
||||
import aqua.definitions.FunctionDef
|
||||
import aqua.res.AquaRes
|
||||
import cats.syntax.show.*
|
||||
|
||||
@ -19,7 +20,8 @@ object AirBackend extends Backend {
|
||||
|
||||
aqua.funcs.toList.map { fr =>
|
||||
val airStr = FuncAirGen(fr).generate.show
|
||||
Generated("." + fr.funcName + ext, docs + airStr, AirString(fr.funcName, airStr) :: Nil)
|
||||
val funcDef = FunctionDef(fr)
|
||||
Generated("." + fr.funcName + ext, docs + airStr, AirFunction(fr.funcName, airStr, funcDef) :: Nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
backend/api/src/main/scala/aqua/backend/api/APIBackend.scala
Normal file
25
backend/api/src/main/scala/aqua/backend/api/APIBackend.scala
Normal file
@ -0,0 +1,25 @@
|
||||
package aqua.backend.api
|
||||
|
||||
import aqua.backend.air.AirBackend
|
||||
import aqua.backend.{Backend, Generated}
|
||||
import aqua.res.AquaRes
|
||||
import aqua.definitions.{LabeledProductTypeDef, ArrowTypeDef, ServiceDef}
|
||||
|
||||
object APIBackend extends Backend {
|
||||
|
||||
override def generate(res: AquaRes): Seq[Generated] =
|
||||
if (res.isEmpty) Nil
|
||||
else {
|
||||
val airGenerated = AirBackend.generate(res)
|
||||
|
||||
val services = res.services.map { srv =>
|
||||
val functions = LabeledProductTypeDef(
|
||||
srv.members.map { case (n, a) => (n, ArrowTypeDef(a)) }
|
||||
)
|
||||
|
||||
ServiceDef(srv.defaultId.map(s => s.replace("\"", "")), functions)
|
||||
}.toList
|
||||
|
||||
Generated("", "", airGenerated.flatMap(_.air).toList, services) :: Nil
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
package aqua.backend
|
||||
package aqua.definitions
|
||||
|
||||
import aqua.res.FuncRes
|
||||
import aqua.types.*
|
||||
import aqua.definitions.*
|
||||
import io.circe.*
|
||||
import io.circe.parser.*
|
||||
import io.circe.syntax.*
|
||||
@ -174,6 +175,8 @@ case class StructTypeDef(name: String, fields: Map[String, TypeDefinition]) exte
|
||||
|
||||
case class LabeledProductTypeDef(fields: List[(String, TypeDefinition)]) extends ProductTypeDef {
|
||||
val tag = "labeledProduct"
|
||||
|
||||
override def toString: String = s"LabeledProduct(${fields.map(_.toString())})"
|
||||
}
|
||||
|
||||
case class UnlabeledProductTypeDef(items: List[TypeDefinition]) extends ProductTypeDef {
|
5
backend/src/main/scala/aqua/backend/AirFunction.scala
Normal file
5
backend/src/main/scala/aqua/backend/AirFunction.scala
Normal file
@ -0,0 +1,5 @@
|
||||
package aqua.backend
|
||||
|
||||
import aqua.definitions.FunctionDef
|
||||
|
||||
case class AirFunction(name: String, air: String, funcDef: FunctionDef)
|
@ -1,3 +0,0 @@
|
||||
package aqua.backend
|
||||
|
||||
case class AirString(name: String, air: String)
|
@ -1,9 +1,11 @@
|
||||
package aqua.backend
|
||||
|
||||
import aqua.definitions.ServiceDef
|
||||
|
||||
/**
|
||||
* Compilation result
|
||||
*
|
||||
* @param suffix extension or another info that will be added to a resulted file
|
||||
* @param content compiled code
|
||||
*/
|
||||
case class Generated(suffix: String, content: String, air: List[AirString])
|
||||
case class Generated(suffix: String, content: String, air: List[AirFunction], services: List[ServiceDef] = Nil)
|
||||
|
@ -6,7 +6,7 @@ import aqua.res.AquaRes
|
||||
|
||||
case class OutputFile(res: AquaRes) {
|
||||
|
||||
def generate(types: Types, isJs: Boolean, isCommonJS: Boolean): (List[AirString], String) = {
|
||||
def generate(types: Types, isJs: Boolean, isCommonJS: Boolean): (List[AirFunction], String) = {
|
||||
import types.*
|
||||
val services = res.services
|
||||
.map(s => OutputService(s, types))
|
||||
|
@ -20,9 +20,10 @@ case class OutputFunc(func: FuncRes, types: Types) {
|
||||
val funcTypes = types.funcType(func)
|
||||
|
||||
import funcTypes.*
|
||||
import TypeDefinition.*
|
||||
import aqua.definitions.TypeDefinition.*
|
||||
import aqua.definitions.*
|
||||
|
||||
def generate: (AirString, String) = {
|
||||
def generate: (AirFunction, String) = {
|
||||
val tsAir = FuncAirGen(func).generate
|
||||
val codeLeftSpace = " " * 20
|
||||
|
||||
@ -30,7 +31,7 @@ case class OutputFunc(func: FuncRes, types: Types) {
|
||||
val funcDef = FunctionDef(func)
|
||||
|
||||
(
|
||||
AirString(func.funcName, script),
|
||||
AirFunction(func.funcName, script, funcDef),
|
||||
s"""${funcTypes.generate}
|
||||
|export function ${func.funcName}(${typed("...args", "any")}) {
|
||||
|
|
||||
|
@ -14,7 +14,8 @@ case class OutputService(srv: ServiceRes, types: Types) {
|
||||
private val serviceTypes = types.serviceType(srv)
|
||||
|
||||
import serviceTypes.*
|
||||
import TypeDefinition._
|
||||
import aqua.definitions.TypeDefinition.*
|
||||
import aqua.definitions.*
|
||||
|
||||
def generate: String =
|
||||
val functions = LabeledProductTypeDef(
|
||||
|
88
build.sbt
88
build.sbt
@ -17,7 +17,7 @@ val scribeV = "3.7.1"
|
||||
name := "aqua-hll"
|
||||
|
||||
val commons = Seq(
|
||||
baseAquaVersion := "0.8.0",
|
||||
baseAquaVersion := "0.9.0",
|
||||
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
|
||||
scalaVersion := dottyVersion,
|
||||
libraryDependencies ++= Seq(
|
||||
@ -42,7 +42,7 @@ commons
|
||||
lazy val cli = crossProject(JSPlatform, JVMPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.in(file("cli"))
|
||||
.in(file("cli/cli"))
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
@ -50,13 +50,13 @@ lazy val cli = crossProject(JSPlatform, JVMPlatform)
|
||||
"com.monovore" %%% "decline-effect" % declineV
|
||||
)
|
||||
)
|
||||
.dependsOn(compiler, `backend-air`, `backend-ts`, io)
|
||||
.dependsOn(compiler, `backend-air`, `backend-ts`, io, definitions, logging, constants, `aqua-run`)
|
||||
|
||||
lazy val cliJS = cli.js
|
||||
.settings(
|
||||
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.ESModule)),
|
||||
scalaJSUseMainModuleInitializer := true
|
||||
)
|
||||
).dependsOn(`js-exports`, `js-imports`)
|
||||
|
||||
lazy val cliJVM = cli.jvm
|
||||
.settings(
|
||||
@ -67,6 +67,13 @@ lazy val cliJVM = cli.jvm
|
||||
)
|
||||
)
|
||||
|
||||
lazy val `aqua-run` = crossProject(JSPlatform, JVMPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.in(file("aqua-run"))
|
||||
.settings(commons: _*)
|
||||
.dependsOn(compiler, `backend-air`, `backend-ts`, io, definitions, logging, constants)
|
||||
|
||||
lazy val io = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
@ -79,8 +86,10 @@ lazy val io = crossProject(JVMPlatform, JSPlatform)
|
||||
)
|
||||
.dependsOn(compiler, parser)
|
||||
|
||||
lazy val ioJS = io.js.dependsOn(`js-imports`)
|
||||
|
||||
lazy val `language-server-api` = project
|
||||
.in(file("language-server-api"))
|
||||
.in(file("language-server/language-server-api"))
|
||||
.enablePlugins(ScalaJSPlugin)
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
@ -95,6 +104,29 @@ lazy val `language-server-api` = project
|
||||
)
|
||||
.dependsOn(compiler.js, io.js)
|
||||
|
||||
lazy val `js-exports` = project
|
||||
.in(file("js/js-exports"))
|
||||
.enablePlugins(ScalaJSPlugin)
|
||||
.settings(commons: _*)
|
||||
.dependsOn(`backend`.js, definitions.js)
|
||||
|
||||
lazy val `js-imports` = project
|
||||
.in(file("js/js-imports"))
|
||||
.enablePlugins(ScalaJSPlugin)
|
||||
.settings(commons: _*)
|
||||
.dependsOn(`js-exports`, transform.js)
|
||||
|
||||
lazy val `aqua-api` = project
|
||||
.in(file("api/aqua-api"))
|
||||
.enablePlugins(ScalaJSPlugin)
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
|
||||
scalaJSUseMainModuleInitializer := true,
|
||||
Test / test := {}
|
||||
)
|
||||
.dependsOn(`js-exports`, `aqua-run`.js, `backend-api`.js)
|
||||
|
||||
lazy val types = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
@ -197,7 +229,42 @@ lazy val backend = crossProject(JVMPlatform, JSPlatform)
|
||||
buildInfoKeys := Seq[BuildInfoKey](version),
|
||||
buildInfoPackage := "aqua.backend"
|
||||
)
|
||||
.dependsOn(res)
|
||||
.dependsOn(res, definitions)
|
||||
|
||||
lazy val definitions = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.in(file("backend/definitions"))
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"io.circe" %%% "circe-core",
|
||||
"io.circe" %%% "circe-generic",
|
||||
"io.circe" %%% "circe-parser"
|
||||
).map(_ % circeVersion)
|
||||
).dependsOn(res, types)
|
||||
|
||||
lazy val logging = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.in(file("utils/logging"))
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %%% "cats-core" % catsV
|
||||
)
|
||||
)
|
||||
|
||||
lazy val constants = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.in(file("utils/constants"))
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %%% "cats-core" % catsV
|
||||
)
|
||||
).dependsOn(parser, raw)
|
||||
|
||||
lazy val `backend-air` = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
@ -206,6 +273,13 @@ lazy val `backend-air` = crossProject(JVMPlatform, JSPlatform)
|
||||
.settings(commons: _*)
|
||||
.dependsOn(backend, transform)
|
||||
|
||||
lazy val `backend-api` = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.in(file("backend/api"))
|
||||
.settings(commons: _*)
|
||||
.dependsOn(backend, transform, `backend-air`)
|
||||
|
||||
lazy val `backend-ts` = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
@ -218,4 +292,4 @@ lazy val `backend-ts` = crossProject(JVMPlatform, JSPlatform)
|
||||
"io.circe" %%% "circe-parser"
|
||||
).map(_ % circeVersion)
|
||||
)
|
||||
.dependsOn(`backend-air`)
|
||||
.dependsOn(`backend-air`, definitions)
|
||||
|
@ -1,175 +0,0 @@
|
||||
package aqua
|
||||
|
||||
import aqua.ErrorRendering.showError
|
||||
import aqua.backend.{ArrowTypeDef, ProductTypeDef, TypeDefinition}
|
||||
import aqua.builder.{AquaFunction, Service}
|
||||
import aqua.compiler.{AquaCompiler, AquaCompilerConf, CompilerAPI}
|
||||
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
|
||||
import aqua.io.AquaFileError
|
||||
import aqua.js.{Conversions, ServiceHandler, TypeDefinitionJs}
|
||||
import aqua.json.{JsonEncoder, TypeValidator}
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.model.{AquaContext, FuncArrow, ServiceModel}
|
||||
import aqua.parser.lift.FileSpan
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.run.RunCommand.logger
|
||||
import aqua.run.{JsonService, Runner}
|
||||
import aqua.types.{ArrowType, NilType, ProductType}
|
||||
import cats.data.Validated.{invalidNec, validNec}
|
||||
import cats.data.{Chain, NonEmptyList, Validated, ValidatedNec}
|
||||
import cats.effect.IO
|
||||
import cats.effect.kernel.{Async, Clock}
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.monad.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.scalajs.js
|
||||
|
||||
// Function compiler
|
||||
class FuncCompiler[F[_]: Files: AquaIO: Async](
|
||||
input: Option[AquaPath],
|
||||
imports: List[Path],
|
||||
transformConfig: TransformConfig,
|
||||
withRunImport: Boolean = false
|
||||
) extends Logging {
|
||||
|
||||
private def findFunctionAndServices(
|
||||
contexts: Chain[AquaContext],
|
||||
func: CliFunc,
|
||||
services: List[JsonService]
|
||||
): ValidatedNec[String, (FuncArrow, List[Service])] =
|
||||
func.ability
|
||||
.fold(
|
||||
contexts
|
||||
.collectFirstSome(_.allFuncs.get(func.name))
|
||||
)(ab => contexts.collectFirstSome(_.abilities.get(ab).flatMap(_.allFuncs.get(func.name))))
|
||||
.map(validNec)
|
||||
.getOrElse(
|
||||
Validated.invalidNec[String, FuncArrow](
|
||||
s"There is no function '${func.ability.map(_ + ".").getOrElse("")}${func.name}' or it is not exported. Check the spelling or see https://fluence.dev/docs/aqua-book/language/header/#export"
|
||||
)
|
||||
)
|
||||
.andThen { func =>
|
||||
findServices(contexts, services).map { l =>
|
||||
(func, l)
|
||||
}
|
||||
}
|
||||
|
||||
private def findServices(
|
||||
contexts: Chain[AquaContext],
|
||||
services: List[JsonService]
|
||||
): ValidatedNec[String, List[Service]] = {
|
||||
services
|
||||
.map(js =>
|
||||
contexts
|
||||
.collectFirstSome(_.services.get(js.name))
|
||||
.map(sm => (js, sm))
|
||||
.map(validNec)
|
||||
.getOrElse(
|
||||
Validated.invalidNec[String, ServiceModel](
|
||||
s"There is no service '${js.name}' (described in json-service file) in aqua source or it is not exported. Check the spelling or see https://fluence.dev/docs/aqua-book/language/header/#export"
|
||||
)
|
||||
)
|
||||
)
|
||||
.sequence
|
||||
.andThen { l =>
|
||||
l.map { case (jsonService: JsonService, sm: ServiceModel) =>
|
||||
val aquaFunctions: ValidatedNec[String, NonEmptyList[AquaFunction]] =
|
||||
jsonService.functions.map { jf =>
|
||||
sm.arrows(jf.name)
|
||||
.map { case arr: ArrowType =>
|
||||
if (arr.domain.isEmpty)
|
||||
TypeValidator
|
||||
.validateTypes(jf.name, arr.codomain, Some(ProductType(jf.resultType :: Nil)))
|
||||
.map { _ =>
|
||||
new AquaFunction {
|
||||
override def fnName: String = jf.name
|
||||
|
||||
override def handler: ServiceHandler = _ => {
|
||||
val converted = arr.codomain.toList match {
|
||||
case h :: _ =>
|
||||
Conversions.ts2aqua(jf.result, TypeDefinitionJs(TypeDefinition(h)))
|
||||
case Nil =>
|
||||
Conversions.ts2aqua(
|
||||
jf.result,
|
||||
TypeDefinitionJs(TypeDefinition(NilType))
|
||||
)
|
||||
}
|
||||
|
||||
js.Promise.resolve(converted)
|
||||
}
|
||||
override def arrow: ArrowTypeDef =
|
||||
ArrowTypeDef(ProductTypeDef(NilType), ProductTypeDef(arr.codomain))
|
||||
}
|
||||
}
|
||||
else
|
||||
invalidNec(s"Json service '${jf.name}' cannot have any arguments")
|
||||
}
|
||||
.getOrElse(
|
||||
Validated.invalidNec[String, AquaFunction](
|
||||
s"There is no function '${jf.name}' in service '${jsonService.name}' in aqua source. Check your 'json-service' options"
|
||||
)
|
||||
)
|
||||
}.sequence
|
||||
|
||||
aquaFunctions.map(funcs => Service(jsonService.serviceId, funcs))
|
||||
}.sequence
|
||||
}
|
||||
}
|
||||
|
||||
private def compileToContext(
|
||||
path: Path,
|
||||
imports: List[Path],
|
||||
config: AquaCompilerConf = AquaCompilerConf(transformConfig.constantsList)
|
||||
) = {
|
||||
val sources = new AquaFileSources[F](path, imports)
|
||||
CompilerAPI
|
||||
.compileToContext[F, AquaFileError, FileModuleId, FileSpan.F](
|
||||
sources,
|
||||
SpanParser.parser,
|
||||
config
|
||||
)
|
||||
.map(_.leftMap(_.map(_.show)))
|
||||
}
|
||||
|
||||
private def compileBuiltins() = {
|
||||
for {
|
||||
path <- PackagePath.builtin.getPath()
|
||||
context <- compileToContext(path, Nil)
|
||||
} yield {
|
||||
context
|
||||
}
|
||||
}
|
||||
|
||||
// Compile and get only one function
|
||||
def compile(
|
||||
func: CliFunc,
|
||||
jsonServices: List[JsonService],
|
||||
withBuiltins: Boolean = false
|
||||
): F[ValidatedNec[String, (FuncArrow, List[Service])]] = {
|
||||
for {
|
||||
prelude <- Prelude.init[F](withRunImport)
|
||||
// compile builtins and add it to context
|
||||
builtinsV <-
|
||||
if (withBuiltins) compileBuiltins()
|
||||
else validNec[String, Chain[AquaContext]](Chain.empty).pure[F]
|
||||
compileResult <- input.map { ap =>
|
||||
// compile only context to wrap and call function later
|
||||
Clock[F].timed(ap.getPath().flatMap(p => compileToContext(p, prelude.importPaths ++ imports)))
|
||||
}.getOrElse((Duration.Zero, validNec[String, Chain[AquaContext]](Chain.empty)).pure[F])
|
||||
(compileTime, contextV) = compileResult
|
||||
} yield {
|
||||
logger.debug(s"Compile time: ${compileTime.toMillis}ms")
|
||||
// add builtins to the end of context
|
||||
contextV.andThen(c => builtinsV.map(bc => c ++ bc)) andThen (c =>
|
||||
findFunctionAndServices(c, func, jsonServices)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.*
|
||||
import aqua.ErrorRendering.showError
|
||||
import aqua.backend.air.{AirBackend, FuncAirGen}
|
||||
import aqua.backend.js.JavaScriptBackend
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.backend.{FunctionDef, Generated}
|
||||
import aqua.builder.{ArgumentGetter, Finisher, ResultPrinter, Service}
|
||||
import aqua.compiler.{AquaCompiled, AquaCompiler}
|
||||
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
|
||||
import aqua.io.{AquaFileError, OutputPrinter}
|
||||
import aqua.js.*
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{AquaContext, FuncArrow}
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.LiteralToken
|
||||
import aqua.parser.lift.FileSpan
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.run.RunConfig
|
||||
import aqua.run.RunOpts.transformConfig
|
||||
import aqua.types.*
|
||||
import cats.data.*
|
||||
import cats.effect.*
|
||||
import cats.effect.kernel.{Async, Clock}
|
||||
import cats.effect.syntax.async.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.list.*
|
||||
import cats.syntax.monad.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Id, Monad, ~>}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future, Promise}
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSConverters.*
|
||||
import scala.scalajs.js.JSON
|
||||
import scala.scalajs.js.annotation.*
|
||||
|
||||
object RunCommand extends Logging {
|
||||
|
||||
def createKeyPair(
|
||||
sk: Option[Array[Byte]]
|
||||
): Future[KeyPair] = {
|
||||
sk.map { arr =>
|
||||
val typedArr = js.typedarray.Uint8Array.from(arr.map(_.toShort).toJSArray)
|
||||
KeyPair.fromEd25519SK(typedArr).toFuture
|
||||
}.getOrElse(KeyPair.randomEd25519().toFuture)
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a function that is located in `input` file with FluenceJS SDK. Returns no output
|
||||
* @param func
|
||||
* function name
|
||||
* @param input
|
||||
* path to an aqua code with a function
|
||||
* @param imports
|
||||
* the sources the input needs
|
||||
*/
|
||||
def run[F[_]: Files: AquaIO: Async](
|
||||
func: CliFunc,
|
||||
input: Option[AquaPath],
|
||||
imports: List[Path],
|
||||
runConfig: RunConfig,
|
||||
transformConfig: TransformConfig
|
||||
): F[ValidatedNec[String, Unit]] = {
|
||||
val funcCompiler = new FuncCompiler[F](input, imports, transformConfig, withRunImport = true)
|
||||
|
||||
for {
|
||||
funcArrowV <- funcCompiler.compile(func, runConfig.jsonServices, true)
|
||||
callResult <- Clock[F].timed {
|
||||
funcArrowV match {
|
||||
case Validated.Valid((funcCallable, jsonServices)) =>
|
||||
val runner =
|
||||
new Runner(func, funcCallable, runConfig.copy(services = runConfig.services ++ jsonServices), transformConfig)
|
||||
runner.run()
|
||||
case i @ Validated.Invalid(_) => i.pure[F]
|
||||
}
|
||||
}
|
||||
(callTime, result) = callResult
|
||||
} yield {
|
||||
logger.debug(s"Call time: ${callTime.toMillis}ms")
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private val builtinServices =
|
||||
aqua.builder.Console() :: aqua.builder.IPFSUploader("ipfs") :: aqua.builder.DeployHelper() :: Nil
|
||||
|
||||
/**
|
||||
* Executes a function with the specified settings
|
||||
* @param common
|
||||
* common settings
|
||||
* @param funcName
|
||||
* function name
|
||||
* @param inputPath
|
||||
* path to a file with a function
|
||||
* @param imports
|
||||
* imports that must be specified for correct compilation
|
||||
* @param args
|
||||
* arguments to pass into a function
|
||||
* @param argumentGetters
|
||||
* services to get argument if it is a variable
|
||||
* @param services
|
||||
* will be registered before calling for correct execution
|
||||
* @return
|
||||
*/
|
||||
def execRun[F[_]: Async](
|
||||
runInfo: RunInfo,
|
||||
): F[ValidatedNec[String, Unit]] = {
|
||||
val common = runInfo.common
|
||||
LogFormatter.initLogger(Some(common.logLevel.compiler))
|
||||
implicit val aio: AquaIO[F] = new AquaFilesIO[F]
|
||||
|
||||
RunCommand
|
||||
.run[F](
|
||||
runInfo.func,
|
||||
runInfo.input,
|
||||
runInfo.imports,
|
||||
RunConfig(common, runInfo.argumentGetters, runInfo.services ++ builtinServices, runInfo.jsonServices, runInfo.pluginsPaths),
|
||||
transformConfig(common.on, common.constants, common.flags.noXor, common.flags.noRelay)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1,230 +0,0 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.backend.air.FuncAirGen
|
||||
import aqua.backend.{FunctionDef, TypeDefinition}
|
||||
import aqua.builder.{ArgumentGetter, Finisher, ResultPrinter, Service}
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.{Conversions, TypeDefinitionJs}
|
||||
import aqua.json.TypeValidator
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{FuncArrow, ValueModel, VarModel}
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, SeqTag}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
import aqua.{CliFunc, VarJson}
|
||||
import cats.data.Validated.{invalidNec, validNec}
|
||||
import cats.data.{Validated, ValidatedNec}
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.partialOrder.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
|
||||
import scala.collection.immutable.SortedMap
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSON
|
||||
|
||||
class Runner(
|
||||
func: CliFunc,
|
||||
funcCallable: FuncArrow,
|
||||
config: RunConfig,
|
||||
transformConfig: TransformConfig
|
||||
) {
|
||||
|
||||
def resultVariableNames(funcCallable: FuncArrow, name: String): List[String] =
|
||||
funcCallable.arrowType.codomain.toList.zipWithIndex.map { case (t, idx) =>
|
||||
name + idx
|
||||
}
|
||||
import aqua.types.Type.typesPartialOrder
|
||||
|
||||
def validateArguments(
|
||||
funcDomain: List[(String, Type)],
|
||||
args: List[ValueRaw]
|
||||
): ValidatedNec[String, Unit] = {
|
||||
if (funcDomain.size != args.length) {
|
||||
invalidNec(
|
||||
s"Number of arguments for the function is incorrect. Expected: ${args.length}. Actual: ${funcDomain.size}"
|
||||
)
|
||||
} else {
|
||||
funcDomain
|
||||
.zip(args)
|
||||
.map { case ((name, lt), rt) =>
|
||||
rt match {
|
||||
case VarRaw(n, _) =>
|
||||
TypeValidator.validateTypes(n, lt, Some(rt.`type`))
|
||||
case _ =>
|
||||
TypeValidator.validateTypes(name, lt, Some(rt.`type`))
|
||||
}
|
||||
|
||||
}
|
||||
.sequence
|
||||
.map(_ => ())
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps function with necessary services, registers services and calls wrapped function with FluenceJS
|
||||
def run[F[_]: Async](): F[ValidatedNec[String, Unit]] = {
|
||||
validateArguments(
|
||||
funcCallable.arrowType.domain.labelledData,
|
||||
func.args
|
||||
) match {
|
||||
case Validated.Valid(_) =>
|
||||
val resultNames = resultVariableNames(funcCallable, config.resultName)
|
||||
val resultPrinterService =
|
||||
ResultPrinter(config.resultPrinterServiceId, config.resultPrinterName, resultNames)
|
||||
val promiseFinisherService =
|
||||
Finisher(config.finisherServiceId, config.finisherFnName)
|
||||
|
||||
val wrappedV = wrapCall(
|
||||
resultPrinterService,
|
||||
promiseFinisherService
|
||||
)
|
||||
|
||||
// call an input function from a generated function
|
||||
wrappedV match {
|
||||
case Validated.Valid((wrapped, getters)) =>
|
||||
genAirAndMakeCall[F](
|
||||
wrapped,
|
||||
resultPrinterService,
|
||||
promiseFinisherService,
|
||||
getters
|
||||
)
|
||||
case i @ Validated.Invalid(_) => i.pure[F]
|
||||
}
|
||||
case v @ Validated.Invalid(_) =>
|
||||
v.pure[F]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Generates air from function, register all services and make a call through FluenceJS
|
||||
private def genAirAndMakeCall[F[_]: Async](
|
||||
wrapped: FuncArrow,
|
||||
consoleService: ResultPrinter,
|
||||
finisherService: Finisher,
|
||||
getters: List[ArgumentGetter]
|
||||
): F[ValidatedNec[String, Unit]] = {
|
||||
// TODO: prob we can turn this Eval into F
|
||||
val funcRes = Transform.funcRes(wrapped, transformConfig).value
|
||||
val definitions = FunctionDef(funcRes)
|
||||
|
||||
val air = FuncAirGen(funcRes).generate.show
|
||||
|
||||
if (config.common.flags.printAir) {
|
||||
OutputPrinter.print(air)
|
||||
}
|
||||
|
||||
FuncCaller.funcCall[F](
|
||||
func.name,
|
||||
air,
|
||||
definitions,
|
||||
config,
|
||||
finisherService,
|
||||
config.services :+ consoleService,
|
||||
getters
|
||||
)
|
||||
}
|
||||
|
||||
private def createGetter(value: VarRaw, arg: js.Dynamic, argType: Type): ArgumentGetter = {
|
||||
val converted = Conversions.ts2aqua(arg, TypeDefinitionJs(TypeDefinition(argType)))
|
||||
ArgumentGetter(value.copy(baseType = argType), converted)
|
||||
}
|
||||
|
||||
// Creates getter services for variables. Return an error if there is no variable in services
|
||||
// and type of this variable couldn't be optional
|
||||
private def getGettersForVars(
|
||||
vars: List[(String, Type)],
|
||||
argGetters: Map[String, VarJson]
|
||||
): ValidatedNec[String, List[ArgumentGetter]] = {
|
||||
vars.map { (n, argType) =>
|
||||
val argGetterOp = argGetters.get(n)
|
||||
(argGetterOp, argType) match {
|
||||
case (None, _) => Validated.invalidNec(s"Unexcepted. There is no service for '$n' argument")
|
||||
// BoxType could be undefined, so, pass service that will return 'undefined' for this argument
|
||||
case (Some(s), _: BoxType) if s._2 == js.undefined =>
|
||||
Validated.validNec(createGetter(s._1, s._2, argType) :: Nil)
|
||||
case (Some(s), _) if s._2 == js.undefined =>
|
||||
Validated.invalidNec(
|
||||
s"Argument '$n' is missing. Expected argument '$n' of type '$argType'"
|
||||
)
|
||||
case (Some(s), _) =>
|
||||
Validated.validNec(createGetter(s._1, s._2, argType) :: Nil)
|
||||
}
|
||||
}.reduceOption(_ combine _).getOrElse(Validated.validNec(Nil))
|
||||
}
|
||||
|
||||
// Wrap a functino like this:
|
||||
// func wrapFunc():
|
||||
// arg1 <- getDataSrv()
|
||||
// arg2 <- getDataSrv()
|
||||
// ...
|
||||
// res <- funcCallable(args:_*)
|
||||
// Console.print(res)
|
||||
// Finisher.finish()
|
||||
private def wrapCall(
|
||||
consoleService: ResultPrinter,
|
||||
finisherService: Finisher
|
||||
): ValidatedNec[String, (FuncArrow, List[ArgumentGetter])] = {
|
||||
val codomain = funcCallable.arrowType.codomain.toList
|
||||
// pass results to a printing service if an input function returns a result
|
||||
// otherwise just call it
|
||||
val body = codomain match {
|
||||
case Nil =>
|
||||
CallArrowRawTag.func(func.name, Call(func.args, Nil)).leaf
|
||||
case types =>
|
||||
val (variables, exports) = types.zipWithIndex.map { case (t, idx) =>
|
||||
val name = config.resultName + idx
|
||||
(VarRaw(name, t), Call.Export(name, t))
|
||||
}.unzip
|
||||
val callFuncTag =
|
||||
CallArrowRawTag.func(func.name, Call(func.args, exports))
|
||||
|
||||
val consoleServiceTag = consoleService.callTag(variables)
|
||||
|
||||
SeqTag.wrap(
|
||||
callFuncTag.leaf,
|
||||
consoleServiceTag.leaf
|
||||
)
|
||||
}
|
||||
|
||||
val finisherServiceTag = finisherService.callTag()
|
||||
|
||||
val vars = func.args
|
||||
.zip(funcCallable.arrowType.domain.toList)
|
||||
.collect { case (VarRaw(n, _), argType) =>
|
||||
(n, argType)
|
||||
}
|
||||
.distinctBy(_._1)
|
||||
|
||||
val gettersV = getGettersForVars(vars, config.argumentGetters)
|
||||
|
||||
gettersV.map { getters =>
|
||||
val gettersTags = getters.map(s => s.callTag().leaf)
|
||||
|
||||
// return something to wait a result if we have return value in function
|
||||
// this is needed to catch an error if it will be occurred
|
||||
val (returnCodomain, ret) = if (codomain.isEmpty) {
|
||||
(NilType, Nil)
|
||||
} else {
|
||||
(UnlabeledConsType(ScalarType.string, NilType), LiteralRaw.quote("ok") :: Nil)
|
||||
}
|
||||
|
||||
(
|
||||
FuncArrow(
|
||||
config.functionWrapperName,
|
||||
SeqTag.wrap((gettersTags :+ body :+ finisherServiceTag.leaf): _*),
|
||||
// no arguments and returns "ok" string
|
||||
ArrowType(NilType, returnCodomain),
|
||||
ret,
|
||||
Map(func.name -> funcCallable),
|
||||
Map.empty,
|
||||
None
|
||||
),
|
||||
getters
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
0
npm/.gitignore → cli/cli-npm/.gitignore
vendored
0
npm/.gitignore → cli/cli-npm/.gitignore
vendored
@ -7,7 +7,7 @@
|
||||
"aqua.js",
|
||||
"index.js",
|
||||
"error.js",
|
||||
"utils.js",
|
||||
"meta-utils.js",
|
||||
"dist/*",
|
||||
"aqua/*"
|
||||
],
|
@ -1,12 +1,13 @@
|
||||
package aqua
|
||||
|
||||
import aqua.builder.ArgumentGetter
|
||||
import aqua.json.JsonEncoder
|
||||
import aqua.js.VarJson
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.{CallArrowToken, CollectionToken, LiteralToken, VarToken}
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.raw.value.{CollectionRaw, LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
import aqua.run.CliFunc
|
||||
import cats.data.*
|
||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, valid, validNec, validNel}
|
||||
import cats.effect.Concurrent
|
||||
@ -16,7 +17,7 @@ import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.semigroup.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Id, Semigroup, ~>}
|
||||
import cats.{~>, Id, Semigroup}
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
@ -25,71 +26,15 @@ import scala.scalajs.js
|
||||
import scala.scalajs.js.JSON
|
||||
|
||||
case class FuncWithData(func: CliFunc, getters: Map[String, VarJson])
|
||||
case class CliFunc(name: String, args: List[ValueRaw] = Nil, ability: Option[String] = None)
|
||||
|
||||
// Variable and its JSON value
|
||||
case class VarJson(variable: VarRaw, value: js.Dynamic)
|
||||
|
||||
object ArgOpts {
|
||||
|
||||
def spanToId: Span.S ~> Id = new (Span.S ~> Id) {
|
||||
|
||||
override def apply[A](span: Span.S[A]): Id[A] = {
|
||||
span._2
|
||||
}
|
||||
}
|
||||
|
||||
// Parses a function name and arguments from a string
|
||||
def funcOpt: Opts[CliFunc] =
|
||||
Opts
|
||||
.option[String]("func", "Function to call with args", "f", "funcName(args)")
|
||||
.mapValidated { str =>
|
||||
CallArrowToken.callArrow.parseAll(str.trim) match {
|
||||
case Right(exprSpan) =>
|
||||
val expr = exprSpan.mapK(spanToId)
|
||||
|
||||
val argsV = expr.args.collect {
|
||||
case LiteralToken(value, ts) =>
|
||||
validNel(LiteralRaw(value, ts))
|
||||
case VarToken(name, _) =>
|
||||
validNel(VarRaw(name.value, BottomType))
|
||||
case CollectionToken(_, values) =>
|
||||
val hasVariables = values.exists {
|
||||
case LiteralToken(_, _) => false
|
||||
case _ => true
|
||||
}
|
||||
if (!hasVariables) {
|
||||
val literals = values.collect { case LiteralToken(value, ts) =>
|
||||
LiteralRaw(value, ts)
|
||||
}
|
||||
val hasSameTypesOrEmpty =
|
||||
literals.isEmpty || literals.map(_.baseType).toSet.size == 1
|
||||
|
||||
if (hasSameTypesOrEmpty) {
|
||||
validNel(
|
||||
NonEmptyList
|
||||
.fromList(literals)
|
||||
.map(l => CollectionRaw(l, ArrayType(l.head.baseType)))
|
||||
.getOrElse(ValueRaw.Nil)
|
||||
)
|
||||
} else
|
||||
invalidNel(
|
||||
"If the argument is an array, then it must contain elements of the same type."
|
||||
)
|
||||
|
||||
} else
|
||||
invalidNel(
|
||||
"Array arguments can only have numbers, strings, or booleans."
|
||||
)
|
||||
case CallArrowToken(_, _, _) =>
|
||||
invalidNel("Function calls as arguments are not supported.")
|
||||
}.sequence
|
||||
argsV.andThen(args =>
|
||||
validNel(CliFunc(expr.funcName.value, args, expr.ability.map(_.name)))
|
||||
)
|
||||
|
||||
case Left(err) => invalid(err.expected.map(_.context.mkString("\n")))
|
||||
}
|
||||
CliFunc.fromString(str)
|
||||
}
|
||||
|
||||
// Gets data from a file or from a json string
|
||||
@ -109,60 +54,14 @@ object ArgOpts {
|
||||
(dataFileOrStringOpt[F], funcOpt).mapN { case (dataF, func) =>
|
||||
dataF.map { dataV =>
|
||||
dataV.andThen { data =>
|
||||
checkDataGetServices(func, data).map { case (funcWithTypedArgs, getters) =>
|
||||
FuncWithData(funcWithTypedArgs, getters)
|
||||
VarJson.checkDataGetServices(func.args, data).map { case (argsWithTypes, getters) =>
|
||||
FuncWithData(func.copy(args = argsWithTypes), getters)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checks if data is presented if there is non-literals in function arguments
|
||||
// creates services to add this data into a call
|
||||
def checkDataGetServices(
|
||||
cliFunc: CliFunc,
|
||||
data: Option[js.Dynamic]
|
||||
): ValidatedNec[String, (CliFunc, Map[String, VarJson])] = {
|
||||
val vars = cliFunc.args.collect { case v @ VarRaw(_, _) =>
|
||||
v
|
||||
// one variable could be used multiple times
|
||||
}.distinctBy(_.name)
|
||||
|
||||
data match {
|
||||
case None if vars.nonEmpty =>
|
||||
// TODO: add a list with actual argument names that where present in the function call
|
||||
invalidNec("Missing variables. You can provide them via --data or --data-path flags")
|
||||
case None =>
|
||||
validNec((cliFunc, Map.empty))
|
||||
case Some(data) =>
|
||||
vars.map { vm =>
|
||||
val arg = {
|
||||
val a = data.selectDynamic(vm.name)
|
||||
if (js.isUndefined(a)) null
|
||||
else a
|
||||
}
|
||||
|
||||
val typeV = JsonEncoder.aquaTypeFromJson(vm.name, arg)
|
||||
|
||||
typeV.map(t => (vm.copy(baseType = t), arg))
|
||||
}.sequence
|
||||
.map(_.map { case (vm, arg) =>
|
||||
vm.name -> VarJson(vm, arg)
|
||||
}.toMap)
|
||||
.andThen { services =>
|
||||
val argsWithTypes = cliFunc.args.map {
|
||||
case v @ VarRaw(n, _) =>
|
||||
// argument getters have been enriched with types derived from JSON
|
||||
// put this types to unriched arguments in CliFunc
|
||||
services.get(n).map(g => v.copy(baseType = g._1.baseType)).getOrElse(v)
|
||||
case v => v
|
||||
}
|
||||
|
||||
validNec((cliFunc.copy(args = argsWithTypes), services))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def dataOpt: Opts[js.Dynamic] =
|
||||
Opts
|
||||
.option[String](
|
@ -1,8 +1,11 @@
|
||||
package aqua
|
||||
|
||||
import aqua.builder.{ArgumentGetter, Service}
|
||||
import aqua.io.{AquaPath, PackagePath}
|
||||
import aqua.js.VarJson
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.run.{GeneralOptions, JsonService, RunCommand, RunOpts}
|
||||
import aqua.run.{CliFunc, GeneralOptions, GeneralOpts, JsonService, RunCommand, RunOpts}
|
||||
import aqua.logging.LogFormatter
|
||||
import cats.data.Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNec}
|
||||
import cats.effect.ExitCode
|
||||
@ -17,28 +20,8 @@ import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scalajs.js
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
sealed trait AquaPath {
|
||||
def getPath[F[_]: Async](): F[Path]
|
||||
}
|
||||
|
||||
// Path for package relative files
|
||||
case class PackagePath(path: String) extends AquaPath {
|
||||
def getPath[F[_]: Async](): F[Path] = PlatformOpts.getPackagePath(path)
|
||||
}
|
||||
|
||||
// Path for absolute or call path relative files
|
||||
case class RelativePath(path: Path) extends AquaPath {
|
||||
def getPath[F[_]: Async](): F[Path] = path.pure[F]
|
||||
}
|
||||
|
||||
object PackagePath {
|
||||
// path to a builtin file in aqua package
|
||||
val builtin: PackagePath = PackagePath("../aqua-lib/builtin.aqua")
|
||||
}
|
||||
|
||||
// All info to run any aqua function
|
||||
case class RunInfo(
|
||||
common: GeneralOptions,
|
||||
@ -109,7 +92,7 @@ object SubCommandBuilder {
|
||||
.valid(
|
||||
name,
|
||||
header,
|
||||
GeneralOptions.opt.map { c =>
|
||||
GeneralOpts.opt.map { c =>
|
||||
RunInfo(c, CliFunc(funcName), Some(path))
|
||||
}
|
||||
)
|
@ -1,6 +1,6 @@
|
||||
package aqua
|
||||
|
||||
import aqua.js.{LogLevel, FluenceJSLogLevel, Meta, Module}
|
||||
import aqua.js.{LogLevel, FluenceJSLogLevel}
|
||||
import fs2.io.file.Path
|
||||
import scribe.Level
|
||||
|
35
cli/cli/.js/src/main/scala/aqua/PlatformOpts.scala
Normal file
35
cli/cli/.js/src/main/scala/aqua/PlatformOpts.scala
Normal file
@ -0,0 +1,35 @@
|
||||
package aqua
|
||||
|
||||
import aqua.config.ConfigOpts
|
||||
import aqua.ipfs.IpfsOpts
|
||||
import aqua.keypair.KeyPairOpts
|
||||
import aqua.remote.{DistOpts, RemoteOpts}
|
||||
import aqua.run.RunOpts
|
||||
import aqua.script.ScriptOpts
|
||||
import cats.data.ValidatedNec
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.monad.*
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.util.Try
|
||||
import cats.effect.std.Console
|
||||
|
||||
// JS-specific options and subcommands
|
||||
object PlatformOpts extends Logging {
|
||||
|
||||
def opts[F[_]: Files: AquaIO: Async: Console]: Opts[F[ValidatedNec[String, Unit]]] =
|
||||
Opts.subcommand(RunOpts.runCommand[F]) orElse
|
||||
Opts.subcommand(KeyPairOpts.command[F]) orElse
|
||||
Opts.subcommand(IpfsOpts.ipfsOpt[F]) orElse
|
||||
Opts.subcommand(ScriptOpts.scriptOpt[F]) orElse
|
||||
Opts.subcommand(RemoteOpts.commands[F]) orElse
|
||||
Opts.subcommand(ConfigOpts.command[F])
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package aqua.air
|
||||
|
||||
import aqua.backend.AirString
|
||||
import aqua.backend.AirFunction
|
||||
import aqua.js.Fluence
|
||||
import cats.data.Validated.{invalid, validNec}
|
||||
import cats.data.{Chain, NonEmptyChain, ValidatedNec}
|
||||
@ -22,7 +22,7 @@ object AirValidation {
|
||||
}
|
||||
|
||||
def validate[F[_]: Async](
|
||||
airs: List[AirString]
|
||||
airs: List[AirFunction]
|
||||
): F[ValidatedNec[String, Unit]] =
|
||||
Async[F].fromFuture {
|
||||
|
@ -6,6 +6,7 @@ import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag}
|
||||
import aqua.raw.value.{LiteralRaw, VarRaw}
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
|
||||
import scala.concurrent.Promise
|
@ -4,6 +4,7 @@ import aqua.backend.*
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.types.ScalarType
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
import scribe.Logging
|
||||
|
@ -4,6 +4,7 @@ import aqua.backend.*
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.types.ScalarType
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
import scribe.Logging
|
||||
|
@ -4,6 +4,7 @@ import aqua.backend.*
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag}
|
||||
import aqua.definitions.*
|
||||
import aqua.raw.value.LiteralRaw
|
||||
import cats.data.NonEmptyList
|
||||
|
@ -4,6 +4,7 @@ import aqua.backend.*
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.types.ScalarType
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
import scribe.Logging
|
||||
|
@ -5,6 +5,7 @@ import aqua.io.OutputPrinter
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag}
|
||||
import aqua.raw.value.{LiteralRaw, VarRaw}
|
||||
import aqua.definitions.*
|
||||
import aqua.types.ScalarType
|
||||
import cats.data.NonEmptyList
|
||||
|
@ -2,6 +2,7 @@ package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.definitions.*
|
||||
import cats.data.NonEmptyList
|
||||
import scribe.Logging
|
||||
|
@ -3,25 +3,22 @@ package aqua.ipfs
|
||||
import aqua.{
|
||||
AppOpts,
|
||||
AquaIO,
|
||||
CliFunc,
|
||||
CommandBuilder,
|
||||
FluenceOpts,
|
||||
LogFormatter,
|
||||
LogLevelTransformer,
|
||||
PackagePath,
|
||||
PlatformOpts,
|
||||
RunInfo,
|
||||
SubCommandBuilder
|
||||
}
|
||||
import aqua.js.{Fluence, PeerConfig}
|
||||
import aqua.keypair.KeyPairShow.show
|
||||
import cats.data.{NonEmptyChain, NonEmptyList, Validated, ValidatedNec, ValidatedNel}
|
||||
import Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import aqua.builder.IPFSUploader
|
||||
import aqua.io.PackagePath
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.model.LiteralModel
|
||||
import aqua.raw.value.LiteralRaw
|
||||
import aqua.run.{GeneralOptions, RunCommand, RunConfig, RunOpts}
|
||||
import aqua.run.{GeneralOptions, RunCommand, RunConfig, RunOpts, GeneralOpts, CliFunc}
|
||||
import cats.effect.{Concurrent, ExitCode, Resource, Sync}
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
@ -56,7 +53,7 @@ object IpfsOpts extends Logging {
|
||||
SubCommandBuilder.valid(
|
||||
"upload",
|
||||
"Upload a file to IPFS",
|
||||
(GeneralOptions.opt, pathOpt).mapN { (common, path) =>
|
||||
(GeneralOpts.opt, pathOpt).mapN { (common, path) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(UploadFuncName, LiteralRaw.quote(path) :: Nil),
|
@ -1,7 +1,5 @@
|
||||
package aqua.ipfs.js
|
||||
|
||||
import aqua.js.FluencePeer
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.annotation.{JSExportAll, JSImport}
|
||||
|
@ -3,10 +3,11 @@ package aqua.remote
|
||||
import aqua.ArgOpts.jsonFromFileOpt
|
||||
import aqua.builder.ArgumentGetter
|
||||
import aqua.raw.value.{LiteralRaw, VarRaw}
|
||||
import aqua.run.GeneralOptions
|
||||
import aqua.run.{GeneralOptions, GeneralOpts, CliFunc}
|
||||
import aqua.types.{ArrayType, ScalarType, StructType}
|
||||
import aqua.*
|
||||
import aqua.json.JsonEncoder
|
||||
import aqua.io.PackagePath
|
||||
import aqua.js.{JsonEncoder, VarJson}
|
||||
import cats.data.{NonEmptyList, NonEmptyMap, ValidatedNec}
|
||||
import cats.data.Validated.{invalidNec, validNec}
|
||||
import cats.effect.{Async, Concurrent, ExitCode, Resource, Sync}
|
||||
@ -60,7 +61,7 @@ object DistOpts extends Logging {
|
||||
SubCommandBuilder.valid(
|
||||
"remove_service",
|
||||
"Remove service",
|
||||
(GeneralOptions.opt, srvIdOpt).mapN { (common, srvId) =>
|
||||
(GeneralOpts.opt, srvIdOpt).mapN { (common, srvId) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(RemoveFuncName, LiteralRaw.quote(srvId) :: Nil),
|
||||
@ -73,7 +74,7 @@ object DistOpts extends Logging {
|
||||
SubCommandBuilder.valid(
|
||||
"create_service",
|
||||
"Deploy service from existing blueprint",
|
||||
(GeneralOptions.opt, blueprintIdOpt).mapN { (common, blueprintId) =>
|
||||
(GeneralOpts.opt, blueprintIdOpt).mapN { (common, blueprintId) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(CreateServiceFuncName, LiteralRaw.quote(blueprintId) :: Nil),
|
||||
@ -86,7 +87,7 @@ object DistOpts extends Logging {
|
||||
SubCommandBuilder.valid(
|
||||
"add_blueprint",
|
||||
"Add blueprint to a peer",
|
||||
(GeneralOptions.opt, blueprintNameOpt, dependencyOpt).mapN {
|
||||
(GeneralOpts.opt, blueprintNameOpt, dependencyOpt).mapN {
|
||||
(common, blueprintName, dependencies) =>
|
||||
val depsWithHash = dependencies.map { d =>
|
||||
if (d.startsWith("hash:"))
|
||||
@ -129,7 +130,7 @@ object DistOpts extends Logging {
|
||||
"deploy_service",
|
||||
"Deploy service from WASM modules",
|
||||
(
|
||||
GeneralOptions.optWithSecretKeyCustomTimeout(60000),
|
||||
GeneralOpts.optWithSecretKeyCustomTimeout(60000),
|
||||
configFromFileOpt[F],
|
||||
srvNameOpt
|
||||
).mapN { (common, configFromFileF, srvName) =>
|
@ -3,14 +3,14 @@ package aqua.remote
|
||||
import aqua.builder.IPFSUploader
|
||||
import DistOpts.*
|
||||
import aqua.ipfs.IpfsOpts.{pathOpt, UploadFuncName}
|
||||
import aqua.js.FluenceEnvironment
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw}
|
||||
import aqua.run.{GeneralOptions, RunCommand, RunConfig, RunOpts}
|
||||
import aqua.run.{GeneralOptions, GeneralOpts, RunCommand, RunConfig, RunOpts, CliFunc}
|
||||
import aqua.*
|
||||
import cats.Applicative
|
||||
import cats.data.{NonEmptyList, Validated}
|
||||
import Validated.{invalidNel, validNel}
|
||||
import aqua.io.PackagePath
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import cats.syntax.applicative.*
|
||||
@ -68,7 +68,7 @@ object RemoteInfoOpts {
|
||||
SubCommandBuilder.valid(
|
||||
"list_interfaces",
|
||||
"List all service interfaces on a peer by a given owner",
|
||||
(GeneralOptions.opt, AppOpts.wrapWithOption(ownerOpt), allFlag).mapN {
|
||||
(GeneralOpts.opt, AppOpts.wrapWithOption(ownerOpt), allFlag).mapN {
|
||||
(common, peer, printAll) =>
|
||||
if (printAll)
|
||||
RunInfo(
|
||||
@ -95,7 +95,7 @@ object RemoteInfoOpts {
|
||||
SubCommandBuilder.valid(
|
||||
GetInterfaceFuncName,
|
||||
"Show interface of a service",
|
||||
(GeneralOptions.opt, idOpt).mapN { (common, serviceId) =>
|
||||
(GeneralOpts.opt, idOpt).mapN { (common, serviceId) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(GetInterfaceFuncName, LiteralRaw.quote(serviceId) :: Nil),
|
||||
@ -108,7 +108,7 @@ object RemoteInfoOpts {
|
||||
SubCommandBuilder.valid(
|
||||
GetModuleInterfaceFuncName,
|
||||
"Print a module interface",
|
||||
(GeneralOptions.opt, idOpt).mapN { (common, serviceId) =>
|
||||
(GeneralOpts.opt, idOpt).mapN { (common, serviceId) =>
|
||||
RunInfo(
|
||||
common,
|
||||
CliFunc(GetModuleInterfaceFuncName, LiteralRaw.quote(serviceId) :: Nil),
|
@ -1,12 +1,13 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.{AppOpts, LogLevels, VarJson}
|
||||
import aqua.AppOpts
|
||||
import aqua.FluenceOpts.*
|
||||
import aqua.builder.{ArgumentGetter, Service}
|
||||
import aqua.config.ConfigOpts.{Krasnodar, Stage, TestNet}
|
||||
import aqua.js.FluenceEnvironment
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.raw.value.VarRaw
|
||||
import aqua.logging.LogLevels
|
||||
import cats.data.{NonEmptyList, Validated}
|
||||
import cats.data.Validated.{invalidNel, validNel}
|
||||
import cats.syntax.applicative.*
|
||||
@ -19,25 +20,7 @@ import scala.concurrent.duration.Duration
|
||||
import scala.scalajs.js
|
||||
import scala.util.Try
|
||||
|
||||
case class Flags(
|
||||
printAir: Boolean,
|
||||
showConfig: Boolean,
|
||||
verbose: Boolean,
|
||||
noXor: Boolean,
|
||||
noRelay: Boolean
|
||||
)
|
||||
|
||||
case class GeneralOptions(
|
||||
timeout: Duration,
|
||||
logLevel: LogLevels,
|
||||
multiaddr: String,
|
||||
on: Option[String],
|
||||
flags: Flags,
|
||||
secretKey: Option[Array[Byte]],
|
||||
constants: List[ConstantRaw]
|
||||
)
|
||||
|
||||
object GeneralOptions {
|
||||
object GeneralOpts {
|
||||
|
||||
val multiaddrOpt: Opts[String] =
|
||||
Opts
|
||||
@ -103,22 +86,7 @@ object GeneralOptions {
|
||||
val opt: Opts[GeneralOptions] = commonOpt(false, false, false)
|
||||
val runOpt: Opts[GeneralOptions] = commonOpt(true, false, true)
|
||||
val optWithSecretKey: Opts[GeneralOptions] = commonOpt(false, true, false)
|
||||
def optWithSecretKeyCustomTimeout(timeoutMs: Int): Opts[GeneralOptions] = commonOpt(false, true, false, Duration(timeoutMs, TimeUnit.MILLISECONDS))
|
||||
}
|
||||
|
||||
// `run` command configuration
|
||||
case class RunConfig(
|
||||
common: GeneralOptions,
|
||||
// services that will pass arguments to air
|
||||
argumentGetters: Map[String, VarJson],
|
||||
// builtin services for aqua run, for example: Console, FileSystem, etc
|
||||
services: List[Service],
|
||||
jsonServices: List[JsonService],
|
||||
plugins: List[String],
|
||||
resultPrinterServiceId: String = "--after-callback-srv-service--",
|
||||
resultPrinterName: String = "console-log",
|
||||
finisherServiceId: String = "--finisher--",
|
||||
finisherFnName: String = "--finish-execution--",
|
||||
resultName: String = "-some-unique-res-name-",
|
||||
functionWrapperName: String = "--someFuncToRun--"
|
||||
)
|
||||
def optWithSecretKeyCustomTimeout(timeoutMs: Int): Opts[GeneralOptions] =
|
||||
commonOpt(false, true, false, Duration(timeoutMs, TimeUnit.MILLISECONDS))
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.LogLevelTransformer
|
||||
import aqua.backend.FunctionDef
|
||||
import aqua.builder.{ArgumentGetter, Finisher, ResultPrinter, Service}
|
||||
import aqua.definitions.FunctionDef
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.*
|
||||
import aqua.keypair.KeyPairShow.show
|
||||
@ -20,7 +20,7 @@ import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.{ExecutionContext, Future, Promise, TimeoutException}
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSConverters.*
|
||||
import scala.scalajs.js.{timers, JSON, JavaScriptException}
|
||||
import scala.scalajs.js.{JSON, JavaScriptException, timers}
|
||||
|
||||
object FuncCaller {
|
||||
|
||||
@ -34,9 +34,11 @@ object FuncCaller {
|
||||
air: String,
|
||||
functionDef: FunctionDef,
|
||||
config: RunConfig,
|
||||
resultPrinterService: ResultPrinter,
|
||||
finisherService: Finisher,
|
||||
services: List[Service],
|
||||
getters: List[ArgumentGetter]
|
||||
getters: List[ArgumentGetter],
|
||||
plugins: List[String]
|
||||
): F[ValidatedNec[String, Unit]] = {
|
||||
|
||||
FluenceUtils.setLogLevel(
|
||||
@ -76,9 +78,9 @@ object FuncCaller {
|
||||
}
|
||||
|
||||
// register all services
|
||||
_ = (services ++ getters :+ finisherService).map(_.register(peer))
|
||||
_ = (services ++ getters :+ finisherService :+ resultPrinterService).map(_.register(peer))
|
||||
// register all plugins
|
||||
plugins <- Plugin.getPlugins(config.plugins)
|
||||
plugins <- Plugin.getPlugins(plugins)
|
||||
_ = plugins.map(_.register(peer))
|
||||
callFuture = CallJsFunction.funcCallJs(
|
||||
air,
|
96
cli/cli/.js/src/main/scala/aqua/run/JsonService.scala
Normal file
96
cli/cli/.js/src/main/scala/aqua/run/JsonService.scala
Normal file
@ -0,0 +1,96 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.ArgOpts.jsonFromFileOpts
|
||||
import aqua.builder.{AquaFunction, ArgumentGetter, Service}
|
||||
import aqua.definitions.{ArrowTypeDef, ProductTypeDef, TypeDefinition}
|
||||
import aqua.js.{Conversions, ServiceHandler, TypeDefinitionJs}
|
||||
import aqua.model.{AquaContext, ServiceModel}
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.{CallArrowToken, CollectionToken, LiteralToken, VarToken}
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.raw.value.{CollectionRaw, LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
import cats.data.*
|
||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, valid, validNec, validNel}
|
||||
import cats.effect.Concurrent
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.semigroup.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Id, Semigroup, ~>}
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
import scala.scalajs.js
|
||||
|
||||
// Description of a service with functions that return structures
|
||||
case class JsonService(name: String, serviceId: String, functions: NonEmptyList[JsonFunction])
|
||||
case class JsonFunction(name: String, result: js.Dynamic, resultType: Type)
|
||||
|
||||
object JsonService {
|
||||
|
||||
def findServices(
|
||||
contexts: Chain[AquaContext],
|
||||
services: List[JsonService]
|
||||
): ValidatedNec[String, List[Service]] = {
|
||||
services
|
||||
.map(js =>
|
||||
contexts
|
||||
.collectFirstSome(_.services.get(js.name))
|
||||
.map(sm => (js, sm))
|
||||
.map(validNec)
|
||||
.getOrElse(
|
||||
Validated.invalidNec[String, ServiceModel](
|
||||
s"There is no service '${js.name}' (described in json-service file) in aqua source or it is not exported. Check the spelling or see https://fluence.dev/docs/aqua-book/language/header/#export"
|
||||
)
|
||||
)
|
||||
)
|
||||
.sequence
|
||||
.andThen { l =>
|
||||
l.map { case (jsonService: JsonService, sm: ServiceModel) =>
|
||||
val aquaFunctions: ValidatedNec[String, NonEmptyList[AquaFunction]] =
|
||||
jsonService.functions.map { jf =>
|
||||
sm.arrows(jf.name)
|
||||
.map { case arr: ArrowType =>
|
||||
if (arr.domain.isEmpty)
|
||||
TypeValidator
|
||||
.validateTypes(jf.name, arr.codomain, Some(ProductType(jf.resultType :: Nil)))
|
||||
.map { _ =>
|
||||
new AquaFunction {
|
||||
override def fnName: String = jf.name
|
||||
|
||||
override def handler: ServiceHandler = _ => {
|
||||
val converted = arr.codomain.toList match {
|
||||
case h :: _ =>
|
||||
Conversions.ts2aqua(jf.result, TypeDefinitionJs(TypeDefinition(h)))
|
||||
case Nil =>
|
||||
Conversions.ts2aqua(
|
||||
jf.result,
|
||||
TypeDefinitionJs(TypeDefinition(NilType))
|
||||
)
|
||||
}
|
||||
|
||||
js.Promise.resolve(converted)
|
||||
}
|
||||
override def arrow: ArrowTypeDef =
|
||||
ArrowTypeDef(ProductTypeDef(NilType), ProductTypeDef(arr.codomain))
|
||||
}
|
||||
}
|
||||
else
|
||||
invalidNec(s"Json service '${jf.name}' cannot have any arguments")
|
||||
}
|
||||
.getOrElse(
|
||||
Validated.invalidNec[String, AquaFunction](
|
||||
s"There is no function '${jf.name}' in service '${jsonService.name}' in aqua source. Check your 'json-service' options"
|
||||
)
|
||||
)
|
||||
}.sequence
|
||||
|
||||
aquaFunctions.map(funcs => Service(jsonService.serviceId, funcs))
|
||||
}.sequence
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -2,7 +2,7 @@ package aqua.run
|
||||
|
||||
import aqua.ArgOpts.jsonFromFileOpts
|
||||
import aqua.builder.ArgumentGetter
|
||||
import aqua.json.JsonEncoder
|
||||
import aqua.js.JsonEncoder
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.{CallArrowToken, CollectionToken, LiteralToken, VarToken}
|
||||
import aqua.parser.lift.Span
|
||||
@ -23,13 +23,10 @@ import fs2.io.file.{Files, Path}
|
||||
|
||||
import scala.scalajs.js
|
||||
|
||||
// Description of a service with functions that return structures
|
||||
case class JsonService(name: String, serviceId: String, functions: NonEmptyList[JsonFunction])
|
||||
case class JsonFunction(name: String, result: js.Dynamic, resultType: Type)
|
||||
object JsonServiceOpts {
|
||||
|
||||
object JsonService {
|
||||
|
||||
def jsonServiceOpt[F[_]: Files: Concurrent]: Opts[F[ValidatedNec[String, NonEmptyList[JsonService]]]] = {
|
||||
def jsonServiceOpt[F[_]: Files: Concurrent]
|
||||
: Opts[F[ValidatedNec[String, NonEmptyList[JsonService]]]] = {
|
||||
jsonFromFileOpts("json-service", "Path to file that describes service with JSON result", "j")
|
||||
.map(b =>
|
||||
b.map { case a: ValidatedNec[String, NonEmptyList[(Path, js.Dynamic)]] =>
|
||||
@ -54,7 +51,9 @@ object JsonService {
|
||||
val fName = f.name
|
||||
val fResult = f.result
|
||||
if (js.isUndefined(fName) || js.typeOf(fName) != "string")
|
||||
invalidNec(s"One of the functions doesn't have a name or it is not a string in JSON service '$path'")
|
||||
invalidNec(
|
||||
s"One of the functions doesn't have a name or it is not a string in JSON service '$path'"
|
||||
)
|
||||
else if (js.isUndefined(fResult))
|
||||
invalidNec(s"Function '$fName' don't have a result in '$path'")
|
||||
else {
|
||||
@ -74,7 +73,9 @@ object JsonService {
|
||||
JsonService(name.asInstanceOf[String], serviceId.asInstanceOf[String], fNEL)
|
||||
)
|
||||
)
|
||||
.getOrElse(invalidNec(s"List of functions in '$name' service is empty in $path"))
|
||||
.getOrElse(
|
||||
invalidNec(s"List of functions in '$name' service is empty in $path")
|
||||
)
|
||||
}
|
||||
}
|
||||
}.sequence
|
229
cli/cli/.js/src/main/scala/aqua/run/RunCommand.scala
Normal file
229
cli/cli/.js/src/main/scala/aqua/run/RunCommand.scala
Normal file
@ -0,0 +1,229 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.*
|
||||
import aqua.ErrorRendering.showError
|
||||
import aqua.backend.air.{AirBackend, FuncAirGen}
|
||||
import aqua.backend.js.JavaScriptBackend
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.backend.Generated
|
||||
import aqua.logging.LogFormatter
|
||||
import aqua.definitions.{FunctionDef, TypeDefinition}
|
||||
import aqua.builder.{ArgumentGetter, Finisher, ResultPrinter, Service}
|
||||
import aqua.compiler.{AquaCompiled, AquaCompiler}
|
||||
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
|
||||
import aqua.io.{AquaFileError, AquaPath, OutputPrinter, Prelude}
|
||||
import aqua.js.*
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{AquaContext, FuncArrow}
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.LiteralToken
|
||||
import aqua.parser.lift.FileSpan
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.run.RunConfig
|
||||
import aqua.run.RunOpts.transformConfig
|
||||
import aqua.types.*
|
||||
import cats.data.*
|
||||
import cats.effect.*
|
||||
import cats.effect.kernel.{Async, Clock}
|
||||
import cats.effect.syntax.async.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.list.*
|
||||
import cats.syntax.monad.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Id, Monad, ~>}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future, Promise}
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSConverters.*
|
||||
import scala.scalajs.js.JSON
|
||||
import scala.scalajs.js.annotation.*
|
||||
|
||||
object RunCommand extends Logging {
|
||||
|
||||
def createKeyPair(
|
||||
sk: Option[Array[Byte]]
|
||||
): Future[KeyPair] = {
|
||||
sk.map { arr =>
|
||||
val typedArr = js.typedarray.Uint8Array.from(arr.map(_.toShort).toJSArray)
|
||||
KeyPair.fromEd25519SK(typedArr).toFuture
|
||||
}.getOrElse(KeyPair.randomEd25519().toFuture)
|
||||
}
|
||||
|
||||
private def createGetter(value: VarRaw, arg: js.Dynamic, argType: Type): ArgumentGetter = {
|
||||
val converted = Conversions.ts2aqua(arg, TypeDefinitionJs(TypeDefinition(argType)))
|
||||
ArgumentGetter(value.copy(baseType = argType), converted)
|
||||
}
|
||||
|
||||
// Creates getter services for variables. Return an error if there is no variable in services
|
||||
// and type of this variable couldn't be optional
|
||||
private def getGettersForVars(
|
||||
vars: List[(String, Type)],
|
||||
argGetters: Map[String, VarJson]
|
||||
): ValidatedNec[String, List[ArgumentGetter]] = {
|
||||
vars.map { (n, argType) =>
|
||||
val argGetterOp = argGetters.get(n)
|
||||
(argGetterOp, argType) match {
|
||||
case (None, _) => Validated.invalidNec(s"Unexcepted. There is no service for '$n' argument")
|
||||
// BoxType could be undefined, so, pass service that will return 'undefined' for this argument
|
||||
case (Some(s), _: BoxType) if s._2 == js.undefined =>
|
||||
Validated.validNec(createGetter(s._1, s._2, argType) :: Nil)
|
||||
case (Some(s), _) if s._2 == js.undefined =>
|
||||
Validated.invalidNec(
|
||||
s"Argument '$n' is missing. Expected argument '$n' of type '$argType'"
|
||||
)
|
||||
case (Some(s), _) =>
|
||||
Validated.validNec(createGetter(s._1, s._2, argType) :: Nil)
|
||||
}
|
||||
}.reduceOption(_ combine _).getOrElse(Validated.validNec(Nil))
|
||||
}
|
||||
|
||||
def resultVariableNames(funcCallable: FuncArrow, name: String): List[String] =
|
||||
funcCallable.arrowType.codomain.toList.zipWithIndex.map { case (t, idx) =>
|
||||
name + idx
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a function that is located in `input` file with FluenceJS SDK. Returns no output
|
||||
* @param func
|
||||
* function name
|
||||
* @param input
|
||||
* path to an aqua code with a function
|
||||
* @param imports
|
||||
* the sources the input needs
|
||||
*/
|
||||
def run[F[_]: Files: AquaIO: Async](
|
||||
func: CliFunc,
|
||||
input: Option[AquaPath],
|
||||
imports: List[Path],
|
||||
runConfig: RunConfig,
|
||||
// services that will pass arguments to air
|
||||
argumentGetters: Map[String, VarJson],
|
||||
// builtin services for aqua run, for example: Console, FileSystem, etc
|
||||
services: List[Service],
|
||||
jsonServices: List[JsonService],
|
||||
plugins: List[String],
|
||||
transformConfig: TransformConfig
|
||||
): F[ValidatedNec[String, Unit]] = {
|
||||
val funcCompiler = new FuncCompiler[F](input, imports, transformConfig)
|
||||
|
||||
for {
|
||||
prelude <- Prelude.init[F](true)
|
||||
contextV <- funcCompiler.compile(prelude.importPaths, true)
|
||||
callResult <- Clock[F].timed {
|
||||
contextV.andThen { context =>
|
||||
FuncCompiler
|
||||
.findFunction(context, func)
|
||||
.andThen(callable =>
|
||||
JsonService
|
||||
.findServices(context, jsonServices)
|
||||
.map(jsonServices => (callable, jsonServices))
|
||||
)
|
||||
}.andThen { case (funcCallable, jsonServices) =>
|
||||
val resultNames = resultVariableNames(funcCallable, runConfig.resultName)
|
||||
val resultPrinterService =
|
||||
ResultPrinter(
|
||||
runConfig.resultPrinterServiceId,
|
||||
runConfig.resultPrinterName,
|
||||
resultNames
|
||||
)
|
||||
val promiseFinisherService =
|
||||
Finisher(runConfig.finisherServiceId, runConfig.finisherFnName)
|
||||
|
||||
val vars = func.args
|
||||
.zip(funcCallable.arrowType.domain.toList)
|
||||
.collect { case (VarRaw(n, _), argType) =>
|
||||
(n, argType)
|
||||
}
|
||||
.distinctBy(_._1)
|
||||
getGettersForVars(vars, argumentGetters).andThen { getters =>
|
||||
val gettersTags = getters.map(s => s.callTag())
|
||||
val preparer =
|
||||
new CallPreparer(
|
||||
func,
|
||||
funcCallable,
|
||||
gettersTags,
|
||||
resultPrinterService.callTag,
|
||||
promiseFinisherService.callTag(),
|
||||
runConfig,
|
||||
transformConfig
|
||||
)
|
||||
preparer.prepare().map { info =>
|
||||
FuncCaller.funcCall[F](
|
||||
info.name,
|
||||
info.air,
|
||||
info.definitions,
|
||||
info.config,
|
||||
resultPrinterService,
|
||||
promiseFinisherService,
|
||||
services ++ jsonServices,
|
||||
getters,
|
||||
plugins
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
} match {
|
||||
case Validated.Valid(f) =>
|
||||
f
|
||||
case i @ Validated.Invalid(_) => i.pure[F]
|
||||
}
|
||||
}
|
||||
(callTime, result) = callResult
|
||||
} yield {
|
||||
logger.debug(s"Call time: ${callTime.toMillis}ms")
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private val builtinServices =
|
||||
aqua.builder
|
||||
.Console() :: aqua.builder.IPFSUploader("ipfs") :: aqua.builder.DeployHelper() :: Nil
|
||||
|
||||
/**
|
||||
* Executes a function with the specified settings
|
||||
* @param common
|
||||
* common settings
|
||||
* @param funcName
|
||||
* function name
|
||||
* @param inputPath
|
||||
* path to a file with a function
|
||||
* @param imports
|
||||
* imports that must be specified for correct compilation
|
||||
* @param args
|
||||
* arguments to pass into a function
|
||||
* @param argumentGetters
|
||||
* services to get argument if it is a variable
|
||||
* @param services
|
||||
* will be registered before calling for correct execution
|
||||
* @return
|
||||
*/
|
||||
def execRun[F[_]: Async](
|
||||
runInfo: RunInfo
|
||||
): F[ValidatedNec[String, Unit]] = {
|
||||
val common = runInfo.common
|
||||
LogFormatter.initLogger(Some(common.logLevel.compiler))
|
||||
implicit val aio: AquaIO[F] = new AquaFilesIO[F]
|
||||
|
||||
RunCommand
|
||||
.run[F](
|
||||
runInfo.func,
|
||||
runInfo.input,
|
||||
runInfo.imports,
|
||||
RunConfig(
|
||||
common
|
||||
),
|
||||
runInfo.argumentGetters,
|
||||
runInfo.services ++ builtinServices,
|
||||
runInfo.jsonServices,
|
||||
runInfo.pluginsPaths,
|
||||
transformConfig(common.on, common.constants, common.flags.noXor, common.flags.noRelay)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
package aqua.run
|
||||
|
||||
import aqua.*
|
||||
import aqua.ArgOpts.checkDataGetServices
|
||||
import aqua.builder.{ArgumentGetter, Service}
|
||||
import aqua.io.{AquaPath, RelativePath}
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.{LiteralToken, VarToken}
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.logging.LogFormatter
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.run.plugin.Plugin
|
||||
@ -56,7 +57,7 @@ object RunOpts extends Logging {
|
||||
AppOpts.wrapWithOption(AppOpts.inputOpts[F]),
|
||||
AppOpts.importOpts[F],
|
||||
ArgOpts.funcWithArgsOpt[F],
|
||||
AppOpts.wrapWithOption(JsonService.jsonServiceOpt),
|
||||
AppOpts.wrapWithOption(JsonServiceOpts.jsonServiceOpt),
|
||||
AppOpts.wrapWithOption(Plugin.opt)
|
||||
).mapN { case (inputF, importF, funcWithArgsF, jsonServiceOp, pluginsOp) =>
|
||||
for {
|
||||
@ -83,7 +84,7 @@ object RunOpts extends Logging {
|
||||
name = "run",
|
||||
header = "Run Aqua code",
|
||||
(
|
||||
GeneralOptions.runOpt,
|
||||
GeneralOpts.runOpt,
|
||||
runOptsCompose[F]
|
||||
).mapN {
|
||||
case (
|
@ -1,16 +1,10 @@
|
||||
package aqua.run.plugin
|
||||
|
||||
import aqua.backend.{
|
||||
ArrowTypeDef,
|
||||
LabeledProductTypeDef,
|
||||
ServiceDef,
|
||||
TopTypeDef,
|
||||
UnlabeledProductTypeDef
|
||||
}
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.run.JsonService
|
||||
import aqua.run.plugin.Plugin.toPromise
|
||||
import aqua.types.TopType
|
||||
import aqua.definitions.*
|
||||
import cats.data.{NonEmptyList, ValidatedNec}
|
||||
import cats.effect.Concurrent
|
||||
import cats.syntax.applicative.*
|
@ -6,8 +6,9 @@ import aqua.backend.Generated
|
||||
import aqua.backend.air.{AirBackend, AirGen, FuncAirGen}
|
||||
import aqua.builder.ArgumentGetter
|
||||
import aqua.compiler.AquaCompiler
|
||||
import aqua.js.VarJson
|
||||
import aqua.io.{PackagePath, Prelude, RelativePath}
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.js.{Fluence, PeerConfig}
|
||||
import aqua.keypair.KeyPairShow.show
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{AquaContext, FuncArrow, LiteralModel}
|
||||
@ -16,7 +17,15 @@ import aqua.raw.ops.{Call, CallArrowRawTag}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.res.{AquaRes, FuncRes}
|
||||
import aqua.run.RunOpts.logger
|
||||
import aqua.run.{GeneralOptions, RunCommand, RunConfig, RunOpts}
|
||||
import aqua.run.{
|
||||
CliFunc,
|
||||
FuncCompiler,
|
||||
GeneralOptions,
|
||||
GeneralOpts,
|
||||
RunCommand,
|
||||
RunConfig,
|
||||
RunOpts
|
||||
}
|
||||
import aqua.types.{ArrowType, LiteralType, NilType, ScalarType}
|
||||
import cats.data.*
|
||||
import cats.data.Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
@ -110,7 +119,7 @@ object ScriptOpts extends Logging {
|
||||
AirGen(funcRes.body).generate.show
|
||||
}
|
||||
|
||||
private def commonScriptOpts = GeneralOptions.commonOpt(false, true, true)
|
||||
private def commonScriptOpts = GeneralOpts.commonOpt(false, true, true)
|
||||
|
||||
private def compileAir[F[_]: Async: AquaIO](
|
||||
input: Path,
|
||||
@ -122,30 +131,31 @@ object ScriptOpts extends Logging {
|
||||
new FuncCompiler[F](
|
||||
Option(RelativePath(input)),
|
||||
imports,
|
||||
tConfig,
|
||||
withRunImport = true
|
||||
tConfig
|
||||
)
|
||||
|
||||
val funcName = funcWithArgs.func.name
|
||||
|
||||
for {
|
||||
callableV <- funcCompiler.compile(funcWithArgs.func, Nil)
|
||||
prelude <- Prelude.init[F](true)
|
||||
contextV <- funcCompiler.compile(prelude.importPaths)
|
||||
wrappedBody = CallArrowRawTag.func(funcName, Call(funcWithArgs.func.args, Nil)).leaf
|
||||
result = callableV
|
||||
.map(callable =>
|
||||
result = contextV
|
||||
.andThen(context => FuncCompiler.findFunction(context, funcWithArgs.func))
|
||||
.map { callable =>
|
||||
generateAir(
|
||||
FuncArrow(
|
||||
funcName + "_scheduled",
|
||||
wrappedBody,
|
||||
ArrowType(NilType, NilType),
|
||||
Nil,
|
||||
Map(funcName -> callable._1),
|
||||
Map(funcName -> callable),
|
||||
Map.empty,
|
||||
None
|
||||
),
|
||||
tConfig
|
||||
)
|
||||
)
|
||||
}
|
||||
} yield result
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua
|
||||
|
||||
import aqua.json.JsonEncoder
|
||||
import aqua.js.JsonEncoder
|
||||
import aqua.types.{ArrayType, LiteralType, OptionType, StructType}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
@ -1,6 +1,5 @@
|
||||
package aqua
|
||||
|
||||
import aqua.json.TypeValidator
|
||||
import aqua.run.TypeValidator
|
||||
import aqua.types.{ArrayType, LiteralType, OptionType, ScalarType, StructType, Type}
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
@ -11,8 +11,5 @@ import scala.concurrent.ExecutionContext
|
||||
|
||||
// Scala-specific options and subcommands
|
||||
object PlatformOpts {
|
||||
|
||||
def opts[F[_]: Files: AquaIO: Async: Console]: Opts[F[ValidatedNec[String, Unit]]] = Opts.never
|
||||
def getGlobalNodeModulePath: List[Path] = Nil
|
||||
def getPackagePath: Option[Path] = None
|
||||
}
|
@ -2,6 +2,7 @@ package aqua
|
||||
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.logging.LogFormatter
|
||||
import aqua.model.transform.TransformConfig
|
||||
import cats.data.Validated
|
||||
import cats.effect.{IO, IOApp, Sync}
|
@ -1,5 +1,5 @@
|
||||
package aqua.air
|
||||
import aqua.backend.AirString
|
||||
import aqua.backend.AirFunction
|
||||
import cats.data.ValidatedNec
|
||||
import cats.effect.Async
|
||||
import cats.data.Validated.validNec
|
||||
@ -10,6 +10,6 @@ object AirValidation {
|
||||
|
||||
def init[F[_]: Async](): F[Unit] = Async[F].pure(())
|
||||
|
||||
def validate[F[_]: Async](airs: List[AirString]): F[ValidatedNec[String, Unit]] = Async[F].pure(validNec(()))
|
||||
def validate[F[_]: Async](airs: List[AirFunction]): F[ValidatedNec[String, Unit]] = Async[F].pure(validNec(()))
|
||||
|
||||
}
|
@ -14,7 +14,7 @@ class SourcesSpec extends AsyncFlatSpec with Matchers {
|
||||
implicit val aquaIO: AquaIO[IO] = AquaFilesIO.summon[IO]
|
||||
|
||||
"AquaFileSources" should "generate correct fileId with imports" in {
|
||||
val path = Path("cli/.jvm/src/test/test-dir/path-test")
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir/path-test")
|
||||
val importPath = path.resolve("imports")
|
||||
|
||||
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
|
||||
@ -48,7 +48,7 @@ class SourcesSpec extends AsyncFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
"AquaFileSources" should "throw an error if there is no import that is indicated in a source" in {
|
||||
val path = Path("cli/.jvm/src/test/test-dir")
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir")
|
||||
val importPath = path.resolve("random/import/path")
|
||||
|
||||
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
|
||||
@ -59,7 +59,7 @@ class SourcesSpec extends AsyncFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
"AquaFileSources" should "find correct imports" in {
|
||||
val srcPath = Path("cli/.jvm/src/test/test-dir/index.aqua")
|
||||
val srcPath = Path("cli/cli/.jvm/src/test/test-dir/index.aqua")
|
||||
val importPath = srcPath.resolve("imports")
|
||||
|
||||
val sourceGen = new AquaFileSources[IO](srcPath, importPath :: Nil)
|
||||
@ -94,7 +94,7 @@ class SourcesSpec extends AsyncFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
"AquaFileSources" should "resolve correct path for target" in {
|
||||
val path = Path("cli/.jvm/src/test/test-dir")
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir")
|
||||
val filePath = path.resolve("some-dir/file.aqua")
|
||||
|
||||
val targetPath = Path("/target/dir/")
|
||||
@ -115,7 +115,7 @@ class SourcesSpec extends AsyncFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
"AquaFileSources" should "resolve correct path for target when file is in current directory" in {
|
||||
val path = Path("cli/.jvm/src/test/test-dir")
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir")
|
||||
val filePath = path.resolve("file.aqua")
|
||||
|
||||
val targetPath = Path("/target/dir/")
|
||||
@ -136,13 +136,13 @@ class SourcesSpec extends AsyncFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
"AquaFileSources" should "write correct file with correct path" in {
|
||||
val path = Path("cli/.jvm/src/test/test-dir")
|
||||
val path = Path("cli/cli/.jvm/src/test/test-dir")
|
||||
val filePath = path.resolve("imports/import.aqua")
|
||||
|
||||
val targetPath = path.resolve("target/")
|
||||
|
||||
// clean up
|
||||
val resultPath = Path("cli/.jvm/src/test/test-dir/target/imports/import_hey.custom")
|
||||
val resultPath = Path("cli/cli/.jvm/src/test/test-dir/target/imports/import_hey.custom")
|
||||
(for {
|
||||
_ <- Files[IO].deleteIfExists(resultPath)
|
||||
sourceGen = new AquaFileSources[IO](path, Nil)
|
@ -12,7 +12,7 @@ import fs2.io.file.{Files, Path}
|
||||
class WriteFileSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"cli" should "compile aqua code in js" in {
|
||||
val src = Path("./cli/.jvm/src/test/aqua")
|
||||
val src = Path("./cli/cli/.jvm/src/test/aqua")
|
||||
val targetTs = Files[IO].createTempDirectory.unsafeRunSync()
|
||||
val targetJs = Files[IO].createTempDirectory.unsafeRunSync()
|
||||
val targetAir = Files[IO].createTempDirectory.unsafeRunSync()
|
@ -7,6 +7,7 @@ import aqua.parser.expr.ConstantExpr
|
||||
import aqua.parser.lift.LiftParser
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.raw.value.LiteralRaw
|
||||
import aqua.constants.Constants
|
||||
import cats.data.Validated.{Invalid, Valid}
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNec, ValidatedNel}
|
||||
import cats.effect.kernel.Async
|
||||
@ -103,22 +104,7 @@ object AppOpts {
|
||||
"NAME=value"
|
||||
)
|
||||
.mapValidated { strs =>
|
||||
val parsed = strs.map(s => ConstantExpr.onlyLiteral.parseAll(s))
|
||||
|
||||
val errors = parsed.zip(strs).collect { case (Left(er), str) =>
|
||||
str
|
||||
}
|
||||
|
||||
NonEmptyList
|
||||
.fromList(errors)
|
||||
.fold(
|
||||
Validated.validNel[String, List[ConstantRaw]](parsed.collect { case Right(v) =>
|
||||
ConstantRaw(v._1.value, LiteralRaw(v._2.value, v._2.ts), false)
|
||||
})
|
||||
) { errors =>
|
||||
val errorMsgs = errors.map(str => s"Invalid constant definition '$str'.")
|
||||
Validated.invalid(errorMsgs)
|
||||
}
|
||||
Constants.parse(strs.toList)
|
||||
}
|
||||
.withDefault(List.empty)
|
||||
|
@ -4,6 +4,7 @@ import aqua.backend.Backend
|
||||
import aqua.backend.air.AirBackend
|
||||
import aqua.backend.js.JavaScriptBackend
|
||||
import aqua.backend.ts.TypeScriptBackend
|
||||
import aqua.logging.LogFormatter
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.model.transform.TransformConfig
|
||||
@ -16,7 +17,7 @@ import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{~>, Functor, Id, Monad}
|
||||
import cats.{Functor, Id, Monad, ~>}
|
||||
import com.monovore.decline
|
||||
import com.monovore.decline.effect.CommandIOApp
|
||||
import com.monovore.decline.effect.CommandIOApp.printHelp
|
||||
@ -132,8 +133,8 @@ object AquaCli extends IOApp with Logging {
|
||||
val bc = TransformConfig(wrapWithXor = !noXor, constants = constants)
|
||||
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
|
||||
}
|
||||
logger.info(s"Aqua Compiler $versionStr")
|
||||
LogFormatter.initLogger(Some(logLevel.compiler))
|
||||
logger.info(s"Aqua Compiler $versionStr")
|
||||
|
||||
(inputF, outputF, importsF).mapN { (i, o, imp) =>
|
||||
i.andThen { input =>
|
@ -1,18 +1,12 @@
|
||||
package aqua
|
||||
|
||||
import aqua.backend.{Backend, Generated}
|
||||
import aqua.compiler.{
|
||||
AirValidator,
|
||||
AquaCompiled,
|
||||
AquaCompiler,
|
||||
AquaCompilerConf,
|
||||
AquaError,
|
||||
CompilerAPI
|
||||
}
|
||||
import aqua.compiler.{AirValidator, AquaCompiled, AquaCompiler, AquaCompilerConf, AquaError, CompilerAPI}
|
||||
import aqua.files.{AquaFileSources, FileModuleId}
|
||||
import aqua.io.Prelude
|
||||
import aqua.io.*
|
||||
import aqua.air.AirValidation
|
||||
import aqua.backend.AirString
|
||||
import aqua.backend.AirFunction
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.model.transform.Transform
|
||||
@ -29,7 +23,7 @@ import cats.syntax.applicative.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.show.*
|
||||
import cats.{~>, Applicative, Eval, Monad, Show}
|
||||
import cats.{Applicative, Eval, Monad, Show, ~>}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
import cats.data.Validated.validNec
|
||||
@ -60,14 +54,14 @@ object AquaPathCompiler extends Logging {
|
||||
if (disableAirValidation) {
|
||||
new AirValidator[F] {
|
||||
override def init(): F[Unit] = Applicative[F].pure(())
|
||||
override def validate(airs: List[AirString]): F[ValidatedNec[String, Unit]] =
|
||||
override def validate(airs: List[AirFunction]): F[ValidatedNec[String, Unit]] =
|
||||
Applicative[F].pure(validNec(()))
|
||||
}
|
||||
} else {
|
||||
new AirValidator[F] {
|
||||
override def init(): F[Unit] = AirValidation.init[F]()
|
||||
override def validate(
|
||||
airs: List[AirString]
|
||||
airs: List[AirFunction]
|
||||
): F[ValidatedNec[String, Unit]] = AirValidation.validate[F](airs)
|
||||
}
|
||||
}
|
65
cli/cli/src/main/scala/aqua/FluenceOpts.scala
Normal file
65
cli/cli/src/main/scala/aqua/FluenceOpts.scala
Normal file
@ -0,0 +1,65 @@
|
||||
package aqua
|
||||
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNel}
|
||||
import aqua.logging.LogLevels
|
||||
import com.monovore.decline.Opts
|
||||
import scribe.Level
|
||||
import cats.syntax.traverse.*
|
||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, valid, validNec, validNel}
|
||||
|
||||
import java.util.Base64
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
object FluenceOpts {
|
||||
|
||||
val timeoutOpt: Opts[Duration] =
|
||||
Opts
|
||||
.option[Int]("timeout", "Request timeout in milliseconds", "t")
|
||||
.map(i => Duration(i, TimeUnit.MILLISECONDS))
|
||||
|
||||
val onOpt: Opts[Option[String]] =
|
||||
AppOpts.wrapWithOption(
|
||||
Opts
|
||||
.option[String](
|
||||
"on",
|
||||
"peerId of the peer that will execute the function. Default: host_peer_id",
|
||||
"o",
|
||||
"peerId"
|
||||
)
|
||||
)
|
||||
|
||||
val showConfigOpt: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("show-config", "Print current configuration on start")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val verboseOpt: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("verbose", "Show additional information about the call")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val secretKeyOpt: Opts[Array[Byte]] =
|
||||
Opts
|
||||
.option[String]("sk", "Ed25519 32-byte secret key in base64", "s", "base64")
|
||||
.mapValidated { s =>
|
||||
val decoder = Base64.getDecoder
|
||||
Validated.catchNonFatal {
|
||||
decoder.decode(s)
|
||||
}.leftMap(t => NonEmptyList.one("secret key isn't a valid base64 string: " + t.getMessage))
|
||||
}
|
||||
|
||||
val printAir: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("print-air", "Prints generated AIR code before function execution")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val logLevelOpt: Opts[LogLevels] =
|
||||
Opts.option[String]("log-level", help = s"Set log level. ${LogLevels.logHelpMessage}").mapValidated {
|
||||
str =>
|
||||
LogLevels.fromString(str)
|
||||
}.withDefault(LogLevels())
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package aqua.compiler
|
||||
|
||||
import aqua.backend.AirString
|
||||
import aqua.backend.AirFunction
|
||||
import cats.data.ValidatedNec
|
||||
|
||||
|
||||
@ -8,6 +8,6 @@ trait AirValidator[F[_]] {
|
||||
def init(): F[Unit]
|
||||
|
||||
def validate(
|
||||
airs: List[AirString]
|
||||
airs: List[AirFunction]
|
||||
): F[ValidatedNec[String, Unit]]
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.compiler
|
||||
|
||||
import aqua.backend.{AirString, Backend}
|
||||
import aqua.backend.{AirFunction, Backend}
|
||||
import aqua.linker.{AquaModule, Linker, Modules}
|
||||
import aqua.model.AquaContext
|
||||
import aqua.parser.lift.{LiftParser, Span}
|
||||
|
@ -1,38 +1,18 @@
|
||||
package aqua
|
||||
|
||||
import aqua.config.ConfigOpts
|
||||
import aqua.ipfs.IpfsOpts
|
||||
import aqua.js.{Meta, Module}
|
||||
import aqua.keypair.KeyPairOpts
|
||||
import aqua.remote.{DistOpts, RemoteOpts}
|
||||
import aqua.run.RunOpts
|
||||
import aqua.script.ScriptOpts
|
||||
import cats.data.ValidatedNec
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
import fs2.io.file.{Files, Path}
|
||||
import aqua.js.{Meta, Module}
|
||||
import scribe.Logging
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.monad.*
|
||||
import com.monovore.decline.Opts
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.util.Try
|
||||
import cats.effect.std.Console
|
||||
|
||||
// JS-specific options and subcommands
|
||||
object PlatformOpts extends Logging {
|
||||
|
||||
def opts[F[_]: Files: AquaIO: Async: Console]: Opts[F[ValidatedNec[String, Unit]]] =
|
||||
Opts.subcommand(RunOpts.runCommand[F]) orElse
|
||||
Opts.subcommand(KeyPairOpts.command[F]) orElse
|
||||
Opts.subcommand(IpfsOpts.ipfsOpt[F]) orElse
|
||||
Opts.subcommand(ScriptOpts.scriptOpt[F]) orElse
|
||||
Opts.subcommand(RemoteOpts.commands[F]) orElse
|
||||
Opts.subcommand(ConfigOpts.command[F])
|
||||
object PlatformPackagePath extends Logging {
|
||||
|
||||
// it could be global installed aqua and local installed, different paths for this
|
||||
def getPackagePath[F[_]: Async](path: String): F[Path] = {
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user