package golem:exec@1.0.0;

interface types {
  /// Supported language types and optional version
  record language {
    /// The language to use
    kind: language-kind,
    /// Optionally further narrow down the language version
    version: option<string>,
  }

  /// Supported languages
  enum language-kind {
    javascript,
    python,
  }

  /// Supported encodings for file contents
  enum encoding {
    utf8,
    base64,
    hex,
  }

  /// Code or data file
  record file {
    /// File name
    name: string,
    /// Raw file contents
    content: list<u8>,
    /// Encoding of `content`, defaults to `utf8`
    encoding: option<encoding>,
  }

  /// Resource limits
  record limits {
    /// Limit the execution time, in milliseconds
    time-ms: option<u64>,
    /// Limit the memory usage, in bytes
    memory-bytes: option<u64>,
    /// Limit the maximum file size, in bytes
    file-size-bytes: option<u64>,
    /// Limit the number of spawned processes
    max-processes: option<u32>,
  }

  /// Execution outcome per stage
  record stage-result {
    /// Standard output
    stdout: string,
    /// Standard error output
    stderr: string,
    /// Exit code of the process, if any
    exit-code: option<s32>,
    /// Signal that caused the process to terminate, if any
    signal: option<string>,
  }

  /// Complete execution result
  record exec-result {
    /// Result of the compilation stage, if any
    compile: option<stage-result>,
    /// Result of the execution stage
    run: stage-result,
    /// Execution time in milliseconds
    time-ms: option<u64>,
    /// Consumed memory in bytes
    memory-bytes: option<u64>,
  }

  /// Execution error types
  variant error {
    /// The chosen langauge is not supported
    unsupported-language,
    /// Compilation failed
    compilation-failed(stage-result),
    /// Execution failed
    runtime-failed(stage-result),
    /// Timed out
    timeout,
    /// Resource limits exceeded
    resource-exceeded,
    /// Internal execution error
    internal(string),
  }

  /// Options for controlling the script runner environment
  record run-options {
    /// optional input to provide to the program.
    stdin: option<string>,
    /// command line arguments passed to the program
    args: option<list<string>>,
    /// a list of environment variables to set for the execution
    env: option<list<tuple<string, string>>>,
    /// optional resource limits for the execution
    limits: option<limits>
  }
}

interface executor {
  use types.{language, file, limits, exec-result, error, run-options};

  /// Blocking, non-streaming execution
  ///
  /// - `lang` specifies the programming language and version.
  /// - `modules` are additional code files to include in the execution context. these can be imported in `snippet` in a language-specific way.
  /// - `snippet` is the top level code to execute.
  /// - `options` is controlling the script runner environment, see the run-options record for more details
  ///
  /// The returned value captures the stdout and stderr of the executed snippet.
  run: func(
    lang: language,
    modules: list<file>,
    snippet: string,
    options: run-options,
  ) -> result<exec-result, error>;

  /// Session for executing multiple code snippets in it.
  ///
  /// The session is preserved between the `run` calls, and it also maintains a set of arbitrary data files
  /// accessible in both the executed snippets and for the caller.
  resource session {
    /// Create a new session for executing code snippets in the specified language, with a set of additional
    /// code files that can be imported in the executed snippets.
    constructor(lang: language, modules: list<file>);

    /// Upload a data file to the session, which can be accessed in the executed snippets through standard file system APIs.
    upload: func(file: file) -> result<_, error>;

    /// Execute a code snippet in the session in a blocking way
    ///
    /// - `snippet` is the top level code to execute.
    /// - `options` is controlling the script runner environment, see the run-options record for more details
    ///
    /// The returned value captures the stdout and stderr of the executed snippet.
    run: func(
      snippet: string,
      options: run-options,
    ) -> result<exec-result, error>;

    /// Downloads a data file from the session.
    download: func(path: string) -> result<list<u8>, error>;

    /// Lists all the data files available in the session. These will include the ones that were `upload`ed and also
    /// any other file created by the executed snippets.
    list-files: func(dir: string) -> result<list<string>, error>;

    /// Sets the current working directory within the session.
    set-working-dir: func(path: string) -> result<_, error>;
  }
}


world exec-library {
  export types;
  export executor;
}
