js/models/TIO/TIORun.js
import axios from 'axios/dist/axios.min.js';
import TIOSerializer from '~/models/TIO/TIOSerializer';
import ErrorManager from '~/helpers/ErrorManager';
import Random from '~/modern/Random';
// Make sure trailing `/` exists
export const TIO_API_ENDPOINT = "https://tio.run/cgi-bin/run/api/";
// Width of IDs
export const TIO_ID_WIDTH = 32;
// No indice in range
export const NoIndice = Symbol('TIO.TIORunError.NoIndice');
/**
* Runs some code on TIO.
*/
export default class TIORun {
/**
* Creates an instance to run some code using TIO
* @param {string} code Code to run
* @param {Language} language Language object for language to execute.
*/
constructor(code, language) {
this.code = code;
this.language = language;
this.token = Random.ofLength(TIO_ID_WIDTH);
}
/**
* Returns a TIOSerializer for the current request
* @return {TIOSerializer} Fully prepared serialization object with run.
*/
serializer() {
let serializer = new TIOSerializer();
serializer.addVariable('lang', this.language.tioId);
serializer.addFile('.code.tio', this.code);
serializer.addFile('.input.tio', '');
serializer.addVariable('args', []);
serializer.addRun();
return serializer;
}
async run() {
let res = await axios.post(
TIO_API_ENDPOINT + this.token,
await this.serializer().serialize(),
{
responseType: 'arraybuffer'
}
);
return new TIOResult(this, res.data);
}
}
// TODO: Use KMP or some better algorithm to scan output.
export class TIOResult {
static Section = {
Output: 0,
Debug: 1
};
/**
* Represents the result of a TIO execution.
*
* Data is in form:
*
* <ID>STDOUT<ID>STDERR<ID>
*
* @param {TIORun} request - TIO request creating result.
* @param {ArrayBuffer} data - TIO result data.
*/
constructor(request, data) {
this._request = request;
this._data = Buffer.from(data);
this._id = this._data.slice(0, TIO_ID_WIDTH);
this._stdout = null;
this._stderr = null;
this._seperators = null;
}
/**
* All non-error output of TIO request.
* @type {string}
*/
get getOutput() {
if (this._stdout !== null) return this._stdout;
this._stdout = this._getIndice(TIOResult.Section.Output).toString('utf-8');
return this._stdout;
}
/**
* Error output of TIO. Empty = no error
* @type {string}
*/
get getError() {
if (this._stderr !== null) return this._stderr;
this._stderr = this._getIndice(TIOResult.Section.Debug).toString('utf-8');
return this._stderr;
}
/**
* Checks if there is an error
* @type {boolean}
*/
get isError() {
return this.getError.substr(-1) !== '0';
}
// TODO: modify to use KMP
_findSeperators() {
this._seperators = [];
let index;
do {
index = this._data.indexOf(this._id, index + TIO_ID_WIDTH);
this._seperators.push(index);
} while (index !== -1);
}
_getIndice(n) {
if (this._seperators === null) this._findSeperators();
let seperatorCount = this._seperators.length;
if (seperatorCount <= n) {
ErrorManager.raise(
`Indice ${n} does not exist. Count is ${seperatorCount}`,
NoIndice);
}
// Returns the matching section
let section = this._data.slice(
this._seperators[n] + TIO_ID_WIDTH,
this._seperators[n + 1]
);
return section;
}
}