Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
appstream/src/as-artifact.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
669 lines (588 sloc)
16.6 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | |
| * | |
| * Copyright (C) 2018-2022 Matthias Klumpp <matthias@tenstral.net> | |
| * | |
| * Licensed under the GNU Lesser General Public License Version 2.1 | |
| * | |
| * This library 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 2.1 of the license, or | |
| * (at your option) any later version. | |
| * | |
| * This library 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 library. If not, see <http://www.gnu.org/licenses/>. | |
| */ | |
| /** | |
| * SECTION:as-artifact | |
| * @short_description: Object describing a artifact artifact | |
| * | |
| * Describes a artifact artifact | |
| * | |
| * See also: #AsArtifact | |
| */ | |
| #include "config.h" | |
| #include "as-artifact.h" | |
| #include "as-artifact-private.h" | |
| #include "as-checksum-private.h" | |
| #include "as-utils-private.h" | |
| typedef struct | |
| { | |
| AsArtifactKind kind; | |
| GPtrArray *locations; | |
| GPtrArray *checksums; | |
| guint64 size[AS_SIZE_KIND_LAST]; | |
| gchar *filename; | |
| GRefString *platform; | |
| AsBundleKind bundle_kind; | |
| } AsArtifactPrivate; | |
| G_DEFINE_TYPE_WITH_PRIVATE (AsArtifact, as_artifact, G_TYPE_OBJECT) | |
| #define GET_PRIVATE(o) (as_artifact_get_instance_private (o)) | |
| /** | |
| * as_size_kind_to_string: | |
| * @size_kind: the #AsSizeKind. | |
| * | |
| * Converts the enumerated value to an text representation. | |
| * | |
| * Returns: string version of @size_kind | |
| **/ | |
| const gchar* | |
| as_size_kind_to_string (AsSizeKind size_kind) | |
| { | |
| if (size_kind == AS_SIZE_KIND_INSTALLED) | |
| return "installed"; | |
| if (size_kind == AS_SIZE_KIND_DOWNLOAD) | |
| return "download"; | |
| return "unknown"; | |
| } | |
| /** | |
| * as_size_kind_from_string: | |
| * @size_kind: the string. | |
| * | |
| * Converts the text representation to an enumerated value. | |
| * | |
| * Returns: an #AsSizeKind or %AS_SIZE_KIND_UNKNOWN for unknown | |
| **/ | |
| AsSizeKind | |
| as_size_kind_from_string (const gchar *size_kind) | |
| { | |
| if (g_strcmp0 (size_kind, "download") == 0) | |
| return AS_SIZE_KIND_DOWNLOAD; | |
| if (g_strcmp0 (size_kind, "installed") == 0) | |
| return AS_SIZE_KIND_INSTALLED; | |
| return AS_SIZE_KIND_UNKNOWN; | |
| } | |
| /** | |
| * as_artifact_kind_from_string: | |
| * @kind: the string. | |
| * | |
| * Converts the text representation to an enumerated value. | |
| * | |
| * Returns: (transfer full): a #AsArtifactKind, or %AS_ARTIFACT_KIND_UNKNOWN for unknown. | |
| * | |
| **/ | |
| AsArtifactKind | |
| as_artifact_kind_from_string (const gchar *kind) | |
| { | |
| if (g_strcmp0 (kind, "source") == 0) | |
| return AS_ARTIFACT_KIND_SOURCE; | |
| if (g_strcmp0 (kind, "binary") == 0) | |
| return AS_ARTIFACT_KIND_BINARY; | |
| return AS_ARTIFACT_KIND_UNKNOWN; | |
| } | |
| /** | |
| * as_artifact_kind_to_string: | |
| * @kind: the #AsArtifactKind. | |
| * | |
| * Converts the enumerated value to an text representation. | |
| * | |
| * Returns: string version of @kind | |
| * | |
| **/ | |
| const gchar* | |
| as_artifact_kind_to_string (AsArtifactKind kind) | |
| { | |
| if (kind == AS_ARTIFACT_KIND_SOURCE) | |
| return "source"; | |
| if (kind == AS_ARTIFACT_KIND_BINARY) | |
| return "binary"; | |
| return NULL; | |
| } | |
| /** | |
| * as_artifact_finalize: | |
| **/ | |
| static void | |
| as_artifact_finalize (GObject *object) | |
| { | |
| AsArtifact *artifact = AS_ARTIFACT (object); | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| g_free (priv->filename); | |
| as_ref_string_release (priv->platform); | |
| g_ptr_array_unref (priv->locations); | |
| g_ptr_array_unref (priv->checksums); | |
| G_OBJECT_CLASS (as_artifact_parent_class)->finalize (object); | |
| } | |
| /** | |
| * as_artifact_init: | |
| **/ | |
| static void | |
| as_artifact_init (AsArtifact *artifact) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| priv->locations = g_ptr_array_new_with_free_func (g_free); | |
| priv->checksums = g_ptr_array_new_with_free_func (g_object_unref); | |
| priv->bundle_kind = AS_BUNDLE_KIND_UNKNOWN; | |
| for (guint i = 0; i < AS_SIZE_KIND_LAST; i++) | |
| priv->size[i] = 0; | |
| } | |
| /** | |
| * as_artifact_set_kind: | |
| * @artifact: a #AsArtifact instance. | |
| * @kind: the #AsArtifactKind, e.g. %AS_ARTIFACT_KIND_SOURCE. | |
| * | |
| * Sets the artifact kind. | |
| * | |
| **/ | |
| void | |
| as_artifact_set_kind (AsArtifact *artifact, AsArtifactKind kind) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| priv->kind = kind; | |
| } | |
| /** | |
| * as_artifact_get_kind: | |
| * @artifact: a #AsArtifact instance. | |
| * | |
| * Gets the artifact kind. | |
| * | |
| * Returns: the #AsArtifactKind | |
| * | |
| **/ | |
| AsArtifactKind | |
| as_artifact_get_kind (AsArtifact *artifact) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| return priv->kind; | |
| } | |
| /** | |
| * as_artifact_get_locations: | |
| * | |
| * Gets the artifact locations, typically URLs. | |
| * | |
| * Returns: (transfer none) (element-type utf8): list of locations | |
| **/ | |
| GPtrArray* | |
| as_artifact_get_locations (AsArtifact *artifact) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| return priv->locations; | |
| } | |
| /** | |
| * as_artifact_add_location: | |
| * @location: An URL of the download location | |
| * | |
| * Adds a artifact location. | |
| **/ | |
| void | |
| as_artifact_add_location (AsArtifact *artifact, const gchar *location) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| g_ptr_array_add (priv->locations, g_strdup (location)); | |
| } | |
| /** | |
| * as_artifact_get_checksums: | |
| * | |
| * Get a list of all checksums we have for this artifact. | |
| * | |
| * Returns: (transfer none) (element-type AsChecksum): an array of #AsChecksum objects. | |
| **/ | |
| GPtrArray* | |
| as_artifact_get_checksums (AsArtifact *artifact) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| return priv->checksums; | |
| } | |
| /** | |
| * as_artifact_get_checksum: | |
| * @artifact: a #AsArtifact instance. | |
| * | |
| * Gets the artifact checksum | |
| * | |
| * Returns: (transfer none) (nullable): an #AsChecksum, or %NULL for not set or invalid | |
| **/ | |
| AsChecksum* | |
| as_artifact_get_checksum (AsArtifact *artifact, AsChecksumKind kind) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| guint i; | |
| for (i = 0; i < priv->checksums->len; i++) { | |
| AsChecksum *cs = AS_CHECKSUM (g_ptr_array_index (priv->checksums, i)); | |
| if (as_checksum_get_kind (cs) == kind) | |
| return cs; | |
| } | |
| return NULL; | |
| } | |
| /** | |
| * as_artifact_add_checksum: | |
| * @artifact: An instance of #AsArtifact. | |
| * @cs: The #AsChecksum. | |
| * | |
| * Add a checksum for the file associated with this artifact. | |
| */ | |
| void | |
| as_artifact_add_checksum (AsArtifact *artifact, AsChecksum *cs) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| g_ptr_array_add (priv->checksums, g_object_ref (cs)); | |
| } | |
| /** | |
| * as_artifact_get_size: | |
| * @artifact: a #AsArtifact instance | |
| * @kind: a #AsSizeKind | |
| * | |
| * Gets the artifact size. | |
| * | |
| * Returns: The size of the given kind of this artifact. | |
| **/ | |
| guint64 | |
| as_artifact_get_size (AsArtifact *artifact, AsSizeKind kind) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| g_return_val_if_fail (kind < AS_SIZE_KIND_LAST, 0); | |
| return priv->size[kind]; | |
| } | |
| /** | |
| * as_artifact_set_size: | |
| * @artifact: a #AsArtifact instance | |
| * @size: a size in bytes, or 0 for unknown | |
| * @kind: a #AsSizeKind | |
| * | |
| * Sets the artifact size for the given kind. | |
| **/ | |
| void | |
| as_artifact_set_size (AsArtifact *artifact, guint64 size, AsSizeKind kind) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| g_return_if_fail (kind < AS_SIZE_KIND_LAST); | |
| g_return_if_fail (kind != 0); | |
| priv->size[kind] = size; | |
| } | |
| /** | |
| * as_artifact_get_platform: | |
| * @artifact: a #AsArtifact instance. | |
| * | |
| * Gets the artifact platform string (e.g. a triplet like "x86_64-linux-gnu"). | |
| * | |
| * Returns: The platform triplet or identifier string. | |
| **/ | |
| const gchar* | |
| as_artifact_get_platform (AsArtifact *artifact) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| return priv->platform; | |
| } | |
| /** | |
| * as_artifact_set_platform: | |
| * @artifact: a #AsArtifact instance. | |
| * @platform: the platform triplet. | |
| * | |
| * Sets the artifact platform triplet or identifier string. | |
| **/ | |
| void | |
| as_artifact_set_platform (AsArtifact *artifact, const gchar *platform) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| as_ref_string_assign_safe (&priv->platform, platform); | |
| } | |
| /** | |
| * as_artifact_get_bundle_kind: | |
| * @artifact: an #AsArtifact instance. | |
| * | |
| * Gets the bundle kind of this artifact. | |
| * | |
| * Returns: the #AsBundleKind | |
| **/ | |
| AsBundleKind | |
| as_artifact_get_bundle_kind (AsArtifact *artifact) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| return priv->bundle_kind; | |
| } | |
| /** | |
| * as_artifact_set_bundle_kind: | |
| * @artifact: an #AsArtifact instance. | |
| * @kind: the #AsBundleKind, e.g. %AS_BUNDLE_KIND_TARBALL. | |
| * | |
| * Sets the bundle kind for this release artifact. | |
| **/ | |
| void | |
| as_artifact_set_bundle_kind (AsArtifact *artifact, AsBundleKind kind) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| priv->bundle_kind = kind; | |
| } | |
| /** | |
| * as_artifact_get_filename: | |
| * @artifact: a #AsArtifact instance. | |
| * | |
| * Gets a suggested filename for the downloaded artifact, | |
| * or %NULL if none is suggested. | |
| * | |
| * Returns: The platform triplet or identifier string. | |
| **/ | |
| const gchar* | |
| as_artifact_get_filename (AsArtifact *artifact) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| return priv->filename; | |
| } | |
| /** | |
| * as_artifact_set_filename: | |
| * @artifact: a #AsArtifact instance. | |
| * @filename: the file name suggestion. | |
| * | |
| * Sets a suggested filename for this artifact after it has been downloaded. | |
| **/ | |
| void | |
| as_artifact_set_filename (AsArtifact *artifact, const gchar *filename) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| as_assign_string_safe (priv->filename, filename); | |
| } | |
| /** | |
| * as_artifact_load_from_xml: | |
| * @artifact: a #AsArtifact instance. | |
| * @ctx: the AppStream document context. | |
| * @node: the XML node. | |
| * @error: a #GError. | |
| * | |
| * Loads artifact data from an XML node. | |
| **/ | |
| gboolean | |
| as_artifact_load_from_xml (AsArtifact *artifact, AsContext *ctx, xmlNode *node, GError **error) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| gchar *str; | |
| str = as_xml_get_prop_value (node, "platform"); | |
| as_ref_string_assign_safe (&priv->platform, str); | |
| g_free (str); | |
| str = as_xml_get_prop_value (node, "type"); | |
| priv->kind = as_artifact_kind_from_string (str); | |
| g_free (str); | |
| str = as_xml_get_prop_value (node, "bundle"); | |
| priv->bundle_kind = as_bundle_kind_from_string (str); | |
| g_free (str); | |
| for (xmlNode *iter = node->children; iter != NULL; iter = iter->next) { | |
| g_autofree gchar *content = NULL; | |
| if (iter->type != XML_ELEMENT_NODE) | |
| continue; | |
| if (g_strcmp0 ((gchar*) iter->name, "location") == 0) { | |
| content = as_xml_get_node_value (iter); | |
| as_artifact_add_location (artifact, content); | |
| } else if (g_strcmp0 ((gchar*) iter->name, "filename") == 0) { | |
| g_free (priv->filename); | |
| priv->filename = as_xml_get_node_value (iter); | |
| } else if (g_strcmp0 ((gchar*) iter->name, "checksum") == 0) { | |
| g_autoptr(AsChecksum) cs = NULL; | |
| cs = as_checksum_new (); | |
| if (as_checksum_load_from_xml (cs, ctx, iter, NULL)) | |
| as_artifact_add_checksum (artifact, cs); | |
| } else if (g_strcmp0 ((gchar*) iter->name, "size") == 0) { | |
| AsSizeKind s_kind; | |
| gchar *prop = as_xml_get_prop_value (iter, "type"); | |
| s_kind = as_size_kind_from_string (prop); | |
| if (s_kind != AS_SIZE_KIND_UNKNOWN) { | |
| guint64 size; | |
| content = as_xml_get_node_value (iter); | |
| size = g_ascii_strtoull (content, NULL, 10); | |
| if (size > 0) | |
| as_artifact_set_size (artifact, size, s_kind); | |
| } | |
| g_free (prop); | |
| } | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| * as_artifact_to_xml_node: | |
| * @artifact: a #AsArtifact instance. | |
| * @ctx: the AppStream document context. | |
| * @root: XML node to attach the new nodes to. | |
| * | |
| * Serializes the data to an XML node. | |
| **/ | |
| void | |
| as_artifact_to_xml_node (AsArtifact *artifact, AsContext *ctx, xmlNode *root) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| xmlNode* n_artifact = NULL; | |
| if (priv->kind == AS_ARTIFACT_KIND_UNKNOWN) | |
| return; | |
| n_artifact = as_xml_add_node (root, "artifact"); | |
| as_xml_add_text_prop (n_artifact, | |
| "type", | |
| as_artifact_kind_to_string (priv->kind)); | |
| if (priv->platform != NULL) { | |
| as_xml_add_text_prop (n_artifact, | |
| "platform", | |
| priv->platform); | |
| } | |
| if (priv->bundle_kind != AS_BUNDLE_KIND_UNKNOWN) { | |
| as_xml_add_text_prop (n_artifact, | |
| "bundle", | |
| as_bundle_kind_to_string (priv->bundle_kind)); | |
| } | |
| /* add location urls */ | |
| for (guint j = 0; j < priv->locations->len; j++) { | |
| const gchar *lurl = (const gchar*) g_ptr_array_index (priv->locations, j); | |
| as_xml_add_text_node (n_artifact, "location", lurl); | |
| } | |
| /* add filename tag */ | |
| as_xml_add_text_node (n_artifact, "filename", priv->filename); | |
| /* add checksum node */ | |
| for (guint j = 0; j < priv->checksums->len; j++) { | |
| AsChecksum *cs = AS_CHECKSUM (g_ptr_array_index (priv->checksums, j)); | |
| as_checksum_to_xml_node (cs, ctx, n_artifact); | |
| } | |
| /* add size node */ | |
| for (guint j = 0; j < AS_SIZE_KIND_LAST; j++) { | |
| guint64 asize = as_artifact_get_size (artifact, j); | |
| if (asize > 0) { | |
| xmlNode *s_node; | |
| g_autofree gchar *size_str = g_strdup_printf ("%" G_GUINT64_FORMAT, asize); | |
| s_node = as_xml_add_text_node (n_artifact, "size", size_str); | |
| as_xml_add_text_prop (s_node, "type", as_size_kind_to_string (j)); | |
| } | |
| } | |
| xmlAddChild (root, n_artifact); | |
| } | |
| /** | |
| * as_artifact_load_from_yaml: | |
| * @artifact: a #AsArtifact instance. | |
| * @ctx: the AppStream document context. | |
| * @node: the YAML node. | |
| * @error: a #GError. | |
| * | |
| * Loads data from a YAML field. | |
| **/ | |
| gboolean | |
| as_artifact_load_from_yaml (AsArtifact *artifact, AsContext *ctx, GNode *node, GError **error) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| for (GNode *n = node->children; n != NULL; n = n->next) { | |
| const gchar *key = as_yaml_node_get_key (n); | |
| if (g_strcmp0 (key, "type") == 0) { | |
| priv->kind = as_artifact_kind_from_string (as_yaml_node_get_value (n)); | |
| } else if (g_strcmp0 (key, "platform") == 0) { | |
| as_ref_string_assign_safe (&priv->platform, | |
| as_yaml_node_get_value (n)); | |
| } else if (g_strcmp0 (key, "bundle") == 0) { | |
| priv->bundle_kind = as_bundle_kind_from_string (as_yaml_node_get_value (n)); | |
| } else if (g_strcmp0 (key, "locations") == 0) { | |
| as_yaml_list_to_str_array (n, priv->locations); | |
| } else if (g_strcmp0 (key, "filename") == 0) { | |
| g_free (priv->filename); | |
| priv->filename = g_strdup (as_yaml_node_get_value (n)); | |
| } else if (g_strcmp0 (key, "checksum") == 0) { | |
| for (GNode *sn = n->children; sn != NULL; sn = sn->next) { | |
| g_autoptr(AsChecksum) cs = as_checksum_new (); | |
| if (as_checksum_load_from_yaml (cs, ctx, sn, NULL)) | |
| as_artifact_add_checksum (artifact, cs); | |
| } | |
| } else if (g_strcmp0 (key, "size") == 0) { | |
| for (GNode *sn = n->children; sn != NULL; sn = sn->next) { | |
| AsSizeKind size_kind = as_size_kind_from_string (as_yaml_node_get_key (sn)); | |
| guint64 asize = g_ascii_strtoull (as_yaml_node_get_value (sn), NULL, 10); | |
| if (size_kind == AS_SIZE_KIND_UNKNOWN) | |
| continue; | |
| if (asize <= 0) | |
| continue; | |
| as_artifact_set_size (artifact, asize, size_kind); | |
| } | |
| } else { | |
| as_yaml_print_unknown ("artifact", key); | |
| } | |
| } | |
| return priv->kind != AS_ARTIFACT_KIND_UNKNOWN; | |
| } | |
| /** | |
| * as_artifact_emit_yaml: | |
| * @artifact: a #AsArtifact instance. | |
| * @ctx: the AppStream document context. | |
| * @emitter: The YAML emitter to emit data on. | |
| * | |
| * Emit YAML data for this object. | |
| **/ | |
| void | |
| as_artifact_emit_yaml (AsArtifact *artifact, AsContext *ctx, yaml_emitter_t *emitter) | |
| { | |
| AsArtifactPrivate *priv = GET_PRIVATE (artifact); | |
| if (priv->kind == AS_ARTIFACT_KIND_UNKNOWN) | |
| return; | |
| as_yaml_mapping_start (emitter); | |
| as_yaml_emit_entry (emitter, | |
| "type", | |
| as_artifact_kind_to_string (priv->kind)); | |
| as_yaml_emit_entry (emitter, | |
| "platform", | |
| priv->platform); | |
| if (priv->bundle_kind != AS_BUNDLE_KIND_UNKNOWN) { | |
| as_yaml_emit_entry (emitter, | |
| "bundle", | |
| as_bundle_kind_to_string (priv->bundle_kind)); | |
| } | |
| /* location URLs */ | |
| as_yaml_emit_sequence_from_str_array (emitter, "locations", priv->locations); | |
| /* filename suggestion */ | |
| as_yaml_emit_entry (emitter, | |
| "filename", | |
| priv->filename); | |
| /* checksums */ | |
| if (priv->locations->len > 0) { | |
| as_yaml_emit_scalar (emitter, "checksum"); | |
| as_yaml_mapping_start (emitter); | |
| for (guint j = 0; j < priv->checksums->len; j++) { | |
| AsChecksum *cs = AS_CHECKSUM (g_ptr_array_index (priv->checksums, j)); | |
| as_checksum_emit_yaml (cs, ctx, emitter); | |
| } | |
| as_yaml_mapping_end (emitter); | |
| } | |
| /* sizes */ | |
| as_yaml_emit_scalar (emitter, "size"); | |
| as_yaml_mapping_start (emitter); | |
| for (guint j = 0; j < AS_SIZE_KIND_LAST; j++) { | |
| guint64 asize = as_artifact_get_size (artifact, j); | |
| if (asize > 0) | |
| as_yaml_emit_entry_uint64 (emitter, as_size_kind_to_string (j), asize); | |
| } | |
| as_yaml_mapping_end (emitter); | |
| as_yaml_mapping_end (emitter); | |
| } | |
| /** | |
| * as_artifact_class_init: | |
| **/ | |
| static void | |
| as_artifact_class_init (AsArtifactClass *klass) | |
| { | |
| GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
| object_class->finalize = as_artifact_finalize; | |
| } | |
| /** | |
| * as_artifact_new: | |
| * | |
| * Creates a new #AsArtifact. | |
| * | |
| * Returns: (transfer full): a #AsArtifact | |
| * | |
| **/ | |
| AsArtifact* | |
| as_artifact_new (void) | |
| { | |
| AsArtifact *artifact; | |
| artifact = g_object_new (AS_TYPE_ARTIFACT, NULL); | |
| return AS_ARTIFACT (artifact); | |
| } |