package borogove;
import haxe.io.Bytes;
import thenshim.Promise;
#if js
import js.html.TextEncoder;
final textEncoder = new TextEncoder();
#end
function setupTrace() {
#if js
haxe.Log.trace = (v, ?infos) -> {
if (js.Syntax.typeof(untyped console) != "undefined" && (untyped console).debug != null) {
final params = infos.customParams ?? [];
infos.customParams = [];
final str: Dynamic = haxe.Log.formatOutput(v, infos);
(untyped console).debug.apply(null, [str].concat(params));
} else if (js.Syntax.typeof(untyped console) != "undefined" && (untyped console).log != null) {
final str = haxe.Log.formatOutput(v, infos);
(untyped console).log(str);
}
}
#end
}
function xmppLinkHeader(url: String) {
return new Promise((resolve, reject) -> {
tink.http.Client.fetch(url, { method: HEAD })
.all()
.handle(function(o) switch o {
case Success(res):
final regex = ~/<(xmpp:[^>]+)>/;
for (link in res.header.get("link")) {
if (regex.match(link)) {
resolve(regex.matched(1));
return;
}
}
reject(null);
case Failure(e):
reject(e);
});
});
}
inline function bytesOfString(s: String) {
#if js
return Bytes.ofData(textEncoder.encode(s).buffer);
#else
return Bytes.ofString(s);
#end
}
// Faster just by specializing to array
inline function existsFast<A>(it:Array<A>, f:(item:A) -> Bool) {
var result = false;
for (x in it) {
if (f(x)) {
result = true;
break;
}
}
return result;
}
@:nullSafety(Strict)
inline function findFast<T>(it:Array<T>, f:(item:T) -> Bool):Null<T> {
var result = null;
for (v in it) {
if (f(v)) {
result = v;
break;
}
}
return result;
}
// Std.downcast doesn't play well with null safety
function downcast<T, S>(value: T, c: Class<S>): Null<S> {
return cast Std.downcast(cast value, cast c);
}
function xmlEscape(s: String) {
// NOTE: using StringTools.htmlEscape breaks things if this is one half of a surrogate pair in an adjacent cdata
return StringTools.replace(StringTools.replace(StringTools.replace(s, "&", "&"), "<", "<"), ">", ">");
}
// Taken from StringTools but modified not to decode + as space
#if (flash || js || cs || python) inline #end function uriDecode(s:String):String {
#if flash
return untyped __global__["decodeURIComponent"](s);
#elseif js
return untyped decodeURIComponent(s);
#elseif cs
return untyped cs.system.Uri.UnescapeDataString(s);
#elseif python
return python.lib.urllib.Parse.unquote(s);
#elseif lua
s = lua.NativeStringTools.gsub(s, "%%(%x%x)", function(h) {
return lua.NativeStringTools.char(lua.Lua.tonumber(h, 16));
});
return s;
#else
final bytes = new haxe.io.BytesBuffer();
var i = 0;
while (i < s.length) {
var c = s.charAt(i);
if (c == "%" && i + 2 < s.length) {
final value = Std.parseInt("0x" + s.substr(i + 1, 2));
if (value != null) {
bytes.addByte(value);
i += 3;
continue;
}
}
bytes.addString(c, UTF8);
i++;
}
return bytes.getBytes().toString(); // UTF-8 decode
#end
}
macro function getGitVersion():haxe.macro.Expr.ExprOf<String> {
#if !display
var process = new sys.io.Process('git', ['describe', '--always']);
if (process.exitCode() != 0) {
var message = process.stderr.readAll().toString();
var pos = haxe.macro.Context.currentPos();
haxe.macro.Context.error("Cannot execute `git describe`. " + message, pos);
}
// read the output of the process
var commitHash:String = process.stdout.readLine();
// Generates a string expression
return macro $v{commitHash};
#else
// `#if display` is used for code completion. In this case returning an
// empty string is good enough; We don't want to call git on every hint.
var commitHash:String = "";
return macro $v{commitHash};
#end
}
class Util {
inline static public function at<T>(arr: Array<T>, i: Int): T {
return arr[i];
}
#if js inline #end static public function findLastIndex<T>(it:Array<T>, f:(item:T) -> Bool):Int {
#if js
return untyped it.findLastIndex(f);
#else
var i = it.length - 1;
while (i > -1) {
if (f(it[i]))
return i;
i--;
}
return -1;
#end
}
inline static public function writeS(o: haxe.io.Output, s: String) {
final b = bytesOfString(s);
o.writeBytes(b, 0, b.length);
}
/**
Convert a String index to a UnicodeString index
**/
#if utf16
@:access(StringTools.utf16CodePointAt)
@:access(StringTools.MIN_SURROGATE_CODE_POINT)
static public function convertIndex(u: UnicodeString, index: Int) {
final s: String = u;
var unicodeOffset = 0;
var nativeOffset = 0;
while (nativeOffset < s.length) {
if (nativeOffset == index) {
return unicodeOffset;
}
var c = StringTools.utf16CodePointAt(s, nativeOffset++);
if (c >= StringTools.MIN_SURROGATE_CODE_POINT) {
nativeOffset++;
}
unicodeOffset++;
}
if (nativeOffset == index) {
return unicodeOffset;
}
throw "No matching index";
}
#else
static public function convertIndex(u: UnicodeString, index: Int) {
return index;
}
#end
}