git » sdk » main » tree

[main] / borogove / Util.hx

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, "&", "&amp;"), "<", "&lt;"), ">", "&gt;");
}

// 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
}