@@ -1,2 +1,3 @@
|
|||||||
/build
|
/build
|
||||||
__pycache__
|
__pycache__
|
||||||
|
.pytest_cache
|
||||||
|
@@ -10,8 +10,8 @@
|
|||||||
G_DEFINE_QUARK(playerctl-formatter-error-quark, playerctl_formatter_error);
|
G_DEFINE_QUARK(playerctl-formatter-error-quark, playerctl_formatter_error);
|
||||||
|
|
||||||
enum token_type {
|
enum token_type {
|
||||||
TOKEN_PASSTHROUGH,
|
|
||||||
TOKEN_VARIABLE,
|
TOKEN_VARIABLE,
|
||||||
|
TOKEN_STRING,
|
||||||
TOKEN_FUNCTION,
|
TOKEN_FUNCTION,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,10 +22,9 @@ struct token {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum parser_state {
|
enum parser_state {
|
||||||
STATE_INSIDE = 0,
|
STATE_EXPRESSION = 0,
|
||||||
STATE_PARAMS_OPEN,
|
STATE_IDENTIFIER,
|
||||||
STATE_PARAMS_CLOSED,
|
STATE_STRING,
|
||||||
STATE_PASSTHROUGH,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _PlayerctlFormatterPrivate {
|
struct _PlayerctlFormatterPrivate {
|
||||||
@@ -79,7 +78,109 @@ static gboolean token_list_contains_key(GList *tokens, const gchar *key) {
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean is_identifier_char(gchar c) {
|
||||||
|
return g_ascii_isalnum(c) || c == '_' || c == ':' || c == '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct token *tokenize_expression(const gchar *format, gint pos, gint *end, GError **error) {
|
||||||
|
GError *tmp_error = NULL;
|
||||||
|
int len = strlen(format);
|
||||||
|
char buf[1028];
|
||||||
|
int buf_len = 0;
|
||||||
|
|
||||||
|
enum parser_state state = STATE_EXPRESSION;
|
||||||
|
|
||||||
|
for (int i = pos; i < len; ++i) {
|
||||||
|
switch (state) {
|
||||||
|
case STATE_EXPRESSION:
|
||||||
|
if (format[i] == ' ') {
|
||||||
|
continue;
|
||||||
|
} else if (format[i] == '"') {
|
||||||
|
state = STATE_STRING;
|
||||||
|
continue;
|
||||||
|
} else if (!is_identifier_char(format[i])) {
|
||||||
|
// TODO return NULL to indicate there is no expression
|
||||||
|
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
||||||
|
"unexpected \"%c\", expected expression (position %d)", format[i], i);
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
state = STATE_IDENTIFIER;
|
||||||
|
buf[buf_len++] = format[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_STRING:
|
||||||
|
if (format[i] == '"') {
|
||||||
|
struct token *ret = token_create(TOKEN_STRING);
|
||||||
|
buf[buf_len] = '\0';
|
||||||
|
ret->data = g_strdup(buf);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
while (i < len && format[i] == ' ') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
*end = i;
|
||||||
|
//printf("string: '%s'\n", ret->data);
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
buf[buf_len++] = format[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_IDENTIFIER:
|
||||||
|
if (format[i] == '(') {
|
||||||
|
struct token *ret = token_create(TOKEN_FUNCTION);
|
||||||
|
buf[buf_len] = '\0';
|
||||||
|
ret->data = g_strdup(buf);
|
||||||
|
i += 1;
|
||||||
|
//printf("function: '%s'\n", ret->data);
|
||||||
|
ret->arg = tokenize_expression(format, i, end, &tmp_error);
|
||||||
|
|
||||||
|
while (*end < len && format[*end] == ' ') {
|
||||||
|
*end += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp_error != NULL) {
|
||||||
|
token_destroy(ret);
|
||||||
|
g_propagate_error(error, tmp_error);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format[*end] != ')') {
|
||||||
|
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
||||||
|
"expecting \")\" (position %d)", *end);
|
||||||
|
}
|
||||||
|
|
||||||
|
*end += 1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
} else if (!is_identifier_char(format[i])) {
|
||||||
|
struct token *ret = token_create(TOKEN_VARIABLE);
|
||||||
|
buf[buf_len] = '\0';
|
||||||
|
ret->data = g_strdup(buf);
|
||||||
|
while (i < len && format[i] == ' ') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
*end = i;
|
||||||
|
//printf("variable: '%s' end='%c'\n", ret->data, format[*end]);
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
buf[buf_len] = format[i];
|
||||||
|
++buf_len;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
||||||
|
"unexpected end of format string");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static GList *tokenize_format(const char *format, GError **error) {
|
static GList *tokenize_format(const char *format, GError **error) {
|
||||||
|
GError *tmp_error = NULL;
|
||||||
GList *tokens = NULL;
|
GList *tokens = NULL;
|
||||||
|
|
||||||
if (format == NULL) {
|
if (format == NULL) {
|
||||||
@@ -96,133 +197,50 @@ static GList *tokenize_format(const char *format, GError **error) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum parser_state state = STATE_PASSTHROUGH;
|
|
||||||
for (int i = 0; i < len; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
if (format[i] == '{' && i < len + 1 && format[i+1] == '{') {
|
if (format[i] == '{' && i < len + 1 && format[i+1] == '{') {
|
||||||
if (state == STATE_INSIDE) {
|
if (buf_len > 0) {
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"unexpected token: \"{{\" (position %d)", i);
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (buf_len != 0) {
|
|
||||||
struct token *token = token_create(TOKEN_PASSTHROUGH);
|
|
||||||
buf[buf_len] = '\0';
|
buf[buf_len] = '\0';
|
||||||
|
buf_len = 0;
|
||||||
|
struct token *token = token_create(TOKEN_STRING);
|
||||||
token->data = g_strdup(buf);
|
token->data = g_strdup(buf);
|
||||||
|
//printf("passthrough: '%s'\n", token->data);
|
||||||
tokens = g_list_append(tokens, token);
|
tokens = g_list_append(tokens, token);
|
||||||
}
|
}
|
||||||
i += 1;
|
|
||||||
buf_len = 0;
|
|
||||||
state = STATE_INSIDE;
|
|
||||||
} else if (format[i] == '}' && i < len + 1 && format[i+1] == '}' && state != STATE_PASSTHROUGH) {
|
|
||||||
if (state == STATE_PARAMS_OPEN) {
|
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"unexpected token: \"}}\" (expected closing parens: \")\" at position %d)", i);
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state != STATE_PARAMS_CLOSED) {
|
i += 2;
|
||||||
buf[buf_len] = '\0';
|
int end = 0;
|
||||||
gchar *name = g_strstrip(g_strdup(buf));
|
struct token *token = tokenize_expression(format, i, &end, &tmp_error);
|
||||||
if (strlen(name) == 0) {
|
if (tmp_error != NULL) {
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"got empty template expression at position %d", i);
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
g_free(name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct token *token = token_create(TOKEN_VARIABLE);
|
|
||||||
token->data = name;
|
|
||||||
tokens = g_list_append(tokens, token);
|
|
||||||
} else if (buf_len > 0) {
|
|
||||||
for (int k = 0; k < buf_len; ++k) {
|
|
||||||
if (buf[k] != ' ') {
|
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"got unexpected input after closing parens at position %d", i - buf_len + k);
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i += 1;
|
|
||||||
buf_len = 0;
|
|
||||||
state = STATE_PASSTHROUGH;
|
|
||||||
} else if (format[i] == '(' && state != STATE_PASSTHROUGH) {
|
|
||||||
if (state == STATE_PARAMS_OPEN) {
|
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"unexpected token: \"(\" at position %d", i);
|
|
||||||
token_list_destroy(tokens);
|
token_list_destroy(tokens);
|
||||||
|
g_propagate_error(error, tmp_error);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (state == STATE_PARAMS_CLOSED) {
|
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"unexpected token: \"(\" at position %d", i);
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buf[buf_len] = '\0';
|
|
||||||
gchar *name = g_strstrip(g_strdup(buf));
|
|
||||||
if (strlen(name) == 0) {
|
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"expected a function name to call at position %d", i);
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
g_free(name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct token *token = token_create(TOKEN_FUNCTION);
|
|
||||||
token->data = name;
|
|
||||||
tokens = g_list_append(tokens, token);
|
tokens = g_list_append(tokens, token);
|
||||||
buf_len = 0;
|
i = end;
|
||||||
state = STATE_PARAMS_OPEN;
|
|
||||||
} else if (format[i] == ')' && state != STATE_PASSTHROUGH) {
|
|
||||||
if (state != STATE_PARAMS_OPEN) {
|
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"unexpected token: \")\" at position %d", i);
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buf[buf_len] = '\0';
|
|
||||||
gchar *name = g_strstrip(g_strdup(buf));
|
|
||||||
if (strlen(name) == 0) {
|
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"expected a function parameter at position %d", i);
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
g_free(name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct token *token = token_create(TOKEN_VARIABLE);
|
|
||||||
token->data = name;
|
|
||||||
|
|
||||||
struct token *fn_token = g_list_last(tokens)->data;
|
while (i < len && format[i] == ' ') {
|
||||||
assert(fn_token != NULL);
|
i++;
|
||||||
assert(fn_token->type == TOKEN_FUNCTION);
|
}
|
||||||
assert(fn_token->arg == NULL);
|
|
||||||
fn_token->arg = token;
|
if (i >= len || format[i] != '}' || format[i+1] != '}') {
|
||||||
buf_len = 0;
|
token_list_destroy(tokens);
|
||||||
state = STATE_PARAMS_CLOSED;
|
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
||||||
|
"expecting \"}}\" (position %d)", i);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
} else if (format[i] == '}' && i < len + 1 && format[i+1] == '}') {
|
||||||
|
assert(FALSE && "TODO");
|
||||||
} else {
|
} else {
|
||||||
buf[buf_len++] = format[i];
|
buf[buf_len++] = format[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == STATE_INSIDE || state == STATE_PARAMS_CLOSED) {
|
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"unmatched opener \"{{\" (expected a matching \"}}\" at the end)");
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
return NULL;
|
|
||||||
} else if (state == STATE_PARAMS_OPEN) {
|
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"unmatched opener \"(\" (expected a matching \")\")");
|
|
||||||
token_list_destroy(tokens);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf_len > 0) {
|
if (buf_len > 0) {
|
||||||
buf[buf_len] = '\0';
|
buf[buf_len] = '\0';
|
||||||
struct token *token = token_create(TOKEN_PASSTHROUGH);
|
struct token *token = token_create(TOKEN_STRING);
|
||||||
token->data = g_strdup(buf);
|
token->data = g_strdup(buf);
|
||||||
tokens = g_list_append(tokens, token);
|
tokens = g_list_append(tokens, token);
|
||||||
}
|
}
|
||||||
@@ -238,6 +256,10 @@ static gchar *helperfn_lc(gchar *key, GVariant *value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gchar *helperfn_uc(gchar *key, GVariant *value) {
|
static gchar *helperfn_uc(gchar *key, GVariant *value) {
|
||||||
|
if (value == NULL) {
|
||||||
|
return g_strdup("");
|
||||||
|
}
|
||||||
|
|
||||||
gchar *printed = pctl_print_gvariant(value);
|
gchar *printed = pctl_print_gvariant(value);
|
||||||
gchar *printed_uc = g_utf8_strup(printed, -1);
|
gchar *printed_uc = g_utf8_strup(printed, -1);
|
||||||
g_free(printed);
|
g_free(printed);
|
||||||
@@ -245,6 +267,10 @@ static gchar *helperfn_uc(gchar *key, GVariant *value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gchar *helperfn_duration(gchar *key, GVariant *value) {
|
static gchar *helperfn_duration(gchar *key, GVariant *value) {
|
||||||
|
if (value == NULL) {
|
||||||
|
return g_strdup("");
|
||||||
|
}
|
||||||
|
|
||||||
// mpris durations are represented as int64 in microseconds
|
// mpris durations are represented as int64 in microseconds
|
||||||
if (!g_variant_type_equal(g_variant_get_type(value), G_VARIANT_TYPE_INT64)) {
|
if (!g_variant_type_equal(g_variant_get_type(value), G_VARIANT_TYPE_INT64)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -269,6 +295,10 @@ static gchar *helperfn_duration(gchar *key, GVariant *value) {
|
|||||||
/* Calls g_markup_escape_text to replace the text with appropriately escaped
|
/* Calls g_markup_escape_text to replace the text with appropriately escaped
|
||||||
characters for XML */
|
characters for XML */
|
||||||
static gchar *helperfn_markup_escape(gchar *key, GVariant *value) {
|
static gchar *helperfn_markup_escape(gchar *key, GVariant *value) {
|
||||||
|
if (value == NULL) {
|
||||||
|
return g_strdup("");
|
||||||
|
}
|
||||||
|
|
||||||
gchar *printed = pctl_print_gvariant(value);
|
gchar *printed = pctl_print_gvariant(value);
|
||||||
gchar *escaped = g_markup_escape_text(printed, -1);
|
gchar *escaped = g_markup_escape_text(printed, -1);
|
||||||
g_free(printed);
|
g_free(printed);
|
||||||
@@ -277,6 +307,10 @@ static gchar *helperfn_markup_escape(gchar *key, GVariant *value) {
|
|||||||
|
|
||||||
static gchar *helperfn_emoji(gchar *key, GVariant *value) {
|
static gchar *helperfn_emoji(gchar *key, GVariant *value) {
|
||||||
g_warning("The emoji() helper function is undocumented and experimental and will change in a future release.");
|
g_warning("The emoji() helper function is undocumented and experimental and will change in a future release.");
|
||||||
|
if (value == NULL) {
|
||||||
|
return g_strdup("");
|
||||||
|
}
|
||||||
|
|
||||||
if (g_strcmp0(key, "status") == 0 &&
|
if (g_strcmp0(key, "status") == 0 &&
|
||||||
g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
|
g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
|
||||||
const gchar *status_str = g_variant_get_string(value, NULL);
|
const gchar *status_str = g_variant_get_string(value, NULL);
|
||||||
@@ -318,73 +352,78 @@ struct template_helper {
|
|||||||
{"emoji", &helperfn_emoji},
|
{"emoji", &helperfn_emoji},
|
||||||
};
|
};
|
||||||
|
|
||||||
static gchar *expand_format(GList *tokens, GVariantDict *context, GError **error) {
|
static GVariant *expand_token(struct token *token, GVariantDict *context, GError **error) {
|
||||||
GString *expanded;
|
GError *tmp_error = NULL;
|
||||||
|
|
||||||
expanded = g_string_new("");
|
switch (token->type) {
|
||||||
GList *next = tokens;
|
case TOKEN_STRING:
|
||||||
while (next != NULL) {
|
return g_variant_new("s", token->data);
|
||||||
struct token *token = next->data;
|
|
||||||
switch (token->type) {
|
case TOKEN_VARIABLE:
|
||||||
case TOKEN_PASSTHROUGH:
|
if (g_variant_dict_contains(context, token->data)) {
|
||||||
expanded = g_string_append(expanded, token->data);
|
return g_variant_dict_lookup_value(context, token->data, NULL);
|
||||||
break;
|
} else {
|
||||||
case TOKEN_VARIABLE:
|
return NULL;
|
||||||
{
|
|
||||||
gchar *name = token->data;
|
|
||||||
if (g_variant_dict_contains(context, name)) {
|
|
||||||
GVariant *value = g_variant_dict_lookup_value(context, name, NULL);
|
|
||||||
if (value != NULL) {
|
|
||||||
gchar *value_str = pctl_print_gvariant(value);
|
|
||||||
expanded = g_string_append(expanded, value_str);
|
|
||||||
g_variant_unref(value);
|
|
||||||
g_free(value_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case TOKEN_FUNCTION:
|
|
||||||
|
case TOKEN_FUNCTION:
|
||||||
{
|
{
|
||||||
// XXX: functions must have an argument and that argument must be a
|
|
||||||
// variable (enforced in the tokenization step)
|
|
||||||
assert(token->arg != NULL);
|
assert(token->arg != NULL);
|
||||||
assert(token->arg->type == TOKEN_VARIABLE);
|
gchar *arg_name = NULL;
|
||||||
|
if (token->type == TOKEN_VARIABLE) {
|
||||||
gboolean found = FALSE;
|
arg_name = token->data;
|
||||||
gchar *fn_name = token->data;
|
|
||||||
gchar *arg_name = token->arg->data;
|
|
||||||
|
|
||||||
for (gsize i = 0; i < LENGTH(helpers); ++i) {
|
|
||||||
if (g_strcmp0(helpers[i].name, fn_name) == 0) {
|
|
||||||
GVariant *value = g_variant_dict_lookup_value(context, arg_name, NULL);
|
|
||||||
if (value != NULL) {
|
|
||||||
gchar *result = helpers[i].func(arg_name, value);
|
|
||||||
if (result != NULL) {
|
|
||||||
expanded = g_string_append(expanded, result);
|
|
||||||
g_free(result);
|
|
||||||
}
|
|
||||||
g_variant_unref(value);
|
|
||||||
}
|
|
||||||
found = TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
GVariant *value = expand_token(token->arg, context, &tmp_error);
|
||||||
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
|
||||||
"unknown template function: %s", fn_name);
|
if (tmp_error != NULL) {
|
||||||
token_list_destroy(tokens);
|
g_propagate_error(error, tmp_error);
|
||||||
g_string_free(expanded, TRUE);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
for (gsize i = 0; i < LENGTH(helpers); ++i) {
|
||||||
}
|
if (g_strcmp0(helpers[i].name, token->data) == 0) {
|
||||||
}
|
gchar *result = helpers[i].func(arg_name, value);
|
||||||
|
if (value != NULL) {
|
||||||
|
g_variant_unref(value);
|
||||||
|
}
|
||||||
|
return g_variant_new("s", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
next = next->next;
|
if (value != NULL) {
|
||||||
|
g_variant_unref(value);
|
||||||
|
}
|
||||||
|
g_set_error(error, playerctl_formatter_error_quark(), 1,
|
||||||
|
"unknown template function: %s", token->data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(FALSE && "not reached");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gchar *expand_format(GList *tokens, GVariantDict *context, GError **error) {
|
||||||
|
GError *tmp_error = NULL;
|
||||||
|
GString *expanded;
|
||||||
|
|
||||||
|
expanded = g_string_new("");
|
||||||
|
GList *t = tokens;
|
||||||
|
for (t = tokens; t != NULL; t = t->next) {
|
||||||
|
GVariant *value = expand_token(t->data, context, &tmp_error);
|
||||||
|
if (tmp_error != NULL) {
|
||||||
|
g_propagate_error(error, tmp_error);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != NULL) {
|
||||||
|
gchar *result = pctl_print_gvariant(value);
|
||||||
|
expanded = g_string_append(expanded, result);
|
||||||
|
g_free(result);
|
||||||
|
g_variant_unref(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
return g_string_free(expanded, FALSE);
|
return g_string_free(expanded, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -35,3 +35,15 @@ async def test_format(bus_address):
|
|||||||
|
|
||||||
cmd = await playerctl.run('metadata --format "{{uc(title)}}"')
|
cmd = await playerctl.run('metadata --format "{{uc(title)}}"')
|
||||||
assert cmd.stdout == TITLE.upper()
|
assert cmd.stdout == TITLE.upper()
|
||||||
|
|
||||||
|
cmd = await playerctl.run('metadata --format "{{uc(lc(title))}}"')
|
||||||
|
assert cmd.stdout == TITLE.upper()
|
||||||
|
|
||||||
|
cmd = await playerctl.run('metadata --format \'{{uc("Hi")}}\'')
|
||||||
|
assert cmd.stdout == "HI"
|
||||||
|
|
||||||
|
cmd = await playerctl.run('metadata --format "{{mpris:length}}"')
|
||||||
|
assert cmd.stdout == "100000"
|
||||||
|
|
||||||
|
cmd = await playerctl.run('metadata --format \'@{{ uc( "hi" ) }} - {{uc( lc( "HO" ) ) }} . {{lc( uc( title ) ) }}@\'')
|
||||||
|
assert cmd.stdout == '@HI - HO . a title@'
|
||||||
|
Reference in New Issue
Block a user