248 lines
6.4 KiB
D
248 lines
6.4 KiB
D
/*
|
|
* Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
|
|
*
|
|
* Licensed under the GNU Lesser General Public License Version 3
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the license, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This software is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this software. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
module ag.utils;
|
|
|
|
import std.stdio : writeln;
|
|
import std.string;
|
|
import std.ascii : letters, digits;
|
|
import std.conv : to;
|
|
import std.random : randomSample;
|
|
import std.range : chain;
|
|
import std.algorithm : startsWith;
|
|
import std.array : appender;
|
|
|
|
|
|
struct ImageSize
|
|
{
|
|
uint width;
|
|
uint height;
|
|
|
|
this (uint w, uint h)
|
|
{
|
|
width = w;
|
|
height = h;
|
|
}
|
|
|
|
this (uint s)
|
|
{
|
|
width = s;
|
|
height = s;
|
|
}
|
|
|
|
string toString () const
|
|
{
|
|
return format ("%sx%s", width, height);
|
|
}
|
|
|
|
uint toInt () const
|
|
{
|
|
if (width > height)
|
|
return width;
|
|
return height;
|
|
}
|
|
|
|
int opCmp (const ImageSize s) const
|
|
{
|
|
// only compares width, should be enough for now
|
|
if (this.width > s.width)
|
|
return 1;
|
|
if (this.width == s.width)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a random alphanumeric string.
|
|
*/
|
|
string randomString (uint len)
|
|
{
|
|
auto asciiLetters = to! (dchar[]) (letters);
|
|
auto asciiDigits = to! (dchar[]) (digits);
|
|
|
|
if (len == 0)
|
|
len = 1;
|
|
|
|
auto res = to!string (randomSample (chain (asciiLetters, asciiDigits), len));
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Check if the locale is a valid locale which we want to include
|
|
* in the resulting metadata. Some locales added just for testing
|
|
* by upstreams should be filtered out.
|
|
*/
|
|
bool localeValid (string locale)
|
|
{
|
|
switch (locale) {
|
|
case "x-test":
|
|
case "xx":
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build a global component ID.
|
|
*
|
|
* The global-id is used as a global, unique identifier for this component.
|
|
* (while the component-ID is local, e.g. for one suite).
|
|
* Its primary usecase is to identify a media directory on the filesystem which is
|
|
* associated with this component.
|
|
**/
|
|
string buildCptGlobalID (string cptid, string checksum, bool allowNoChecksum = false)
|
|
{
|
|
if (cptid is null)
|
|
return null;
|
|
if ((!allowNoChecksum) && (checksum is null))
|
|
return null;
|
|
if (checksum is null)
|
|
checksum = "";
|
|
|
|
string gid;
|
|
string[] parts = null;
|
|
if (startsWith (cptid, "org.", "net.", "com.", "io.", "edu.", "name.")) {
|
|
parts = cptid.split (".");
|
|
}
|
|
|
|
if ((parts !is null) && (parts.length > 2))
|
|
gid = format ("%s/%s/%s/%s", parts[0].toLower(), parts[1], join (parts[2..$], "."), checksum);
|
|
else
|
|
gid = format ("%s/%s/%s/%s", cptid[0].toLower(), cptid[0..2].toLower(), cptid, checksum);
|
|
|
|
return gid;
|
|
}
|
|
|
|
/**
|
|
* Get the component-id back from a global component-id.
|
|
*/
|
|
string getCidFromGlobalID (string gcid)
|
|
{
|
|
auto parts = gcid.split ("/");
|
|
if (parts.length != 4)
|
|
return null;
|
|
if (startsWith (parts[0], "org", "net", "com", "io", "edu.", "name")) {
|
|
return join (parts[0..3], ".");
|
|
}
|
|
|
|
return parts[2];
|
|
}
|
|
|
|
/**
|
|
* Copy a directory using parallelism.
|
|
*
|
|
* Original (c) Jay Norwood
|
|
*/
|
|
void copyDir (in string srcDir, in string destDir)
|
|
{
|
|
import std.file;
|
|
import std.path;
|
|
import std.parallelism;
|
|
|
|
auto deSrc = DirEntry (srcDir);
|
|
string[] files;
|
|
|
|
if (!exists (destDir)) {
|
|
mkdirRecurse (destDir); // makes dest root and all required parents
|
|
}
|
|
|
|
auto destDe = DirEntry (destDir);
|
|
if(!destDe.isDir ()) {
|
|
throw new FileException (destDe.name, " is not a directory");
|
|
}
|
|
|
|
string destName = destDe.name ~ '/';
|
|
string destRoot = destName;
|
|
|
|
if (!deSrc.isDir ()) {
|
|
std.file.copy (deSrc.name, destRoot);
|
|
} else {
|
|
auto srcLen = deSrc.name.length;
|
|
if (!std.file.exists (destRoot))
|
|
mkdir (destRoot);
|
|
|
|
// make an array of the regular files only, also create the directory structure
|
|
// Since it is SpanMode.breadth, we can just use mkdir
|
|
foreach (DirEntry e; dirEntries (deSrc.name, SpanMode.breadth, false)) {
|
|
if (attrIsDir (e.linkAttributes)) {
|
|
auto childDir = destRoot ~ e.name[srcLen..$];
|
|
mkdir (childDir);
|
|
} else {
|
|
files ~= e.name;
|
|
}
|
|
}
|
|
|
|
// parallel foreach for regular files
|
|
foreach (fn; taskPool.parallel (files,100)) {
|
|
string dfn = destRoot ~ fn[srcLen..$];
|
|
std.file.copy (fn,dfn);
|
|
}
|
|
}
|
|
}
|
|
|
|
S escapeXml (S) (S s)
|
|
{
|
|
string r;
|
|
size_t lastI;
|
|
auto result = appender!S ();
|
|
|
|
foreach (i, c; s) {
|
|
switch (c) {
|
|
case '&': r = "&"; break;
|
|
case '"': r = """; break;
|
|
case '\'': r = "'"; break;
|
|
case '<': r = "<"; break;
|
|
case '>': r = ">"; break;
|
|
default: continue;
|
|
}
|
|
|
|
// Replace with r
|
|
result.put (s[lastI .. i]);
|
|
result.put (r);
|
|
lastI = i + 1;
|
|
}
|
|
|
|
if (!result.data.ptr)
|
|
return s;
|
|
result.put (s[lastI .. $]);
|
|
return result.data;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
writeln ("TEST: ", "GCID");
|
|
|
|
assert (buildCptGlobalID ("foobar.desktop", "DEADBEEF") == "f/fo/foobar.desktop/DEADBEEF");
|
|
assert (buildCptGlobalID ("org.gnome.yelp.desktop", "DEADBEEF") == "org/gnome/yelp.desktop/DEADBEEF");
|
|
assert (buildCptGlobalID ("noto-cjk.font", "DEADBEEF") == "n/no/noto-cjk.font/DEADBEEF");
|
|
assert (buildCptGlobalID ("io.sample.awesomeapp.sdk", "ABAD1DEA") == "io/sample/awesomeapp.sdk/ABAD1DEA");
|
|
|
|
assert (buildCptGlobalID ("io.sample.awesomeapp.sdk", null, true) == "io/sample/awesomeapp.sdk/");
|
|
|
|
assert (getCidFromGlobalID ("f/fo/foobar.desktop/DEADBEEF") == "foobar.desktop");
|
|
assert (getCidFromGlobalID ("org/gnome/yelp.desktop/DEADBEEF") == "org.gnome.yelp.desktop");
|
|
|
|
assert (ImageSize (80, 40).toString () == "80x40");
|
|
assert (ImageSize (1024, 420).toInt () == 1024);
|
|
assert (ImageSize (1024, 800) > ImageSize (64, 32));
|
|
assert (ImageSize (48) < ImageSize (64));
|
|
}
|