LCOV - code coverage report
Current view: top level - libmogwai-tariff - tariff-builder.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 95 96 99.0 %
Date: 2022-06-30 20:59:16 Functions: 15 15 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 39 54 72.2 %

           Branch data     Line data    Source code
       1                 :            : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
       2                 :            :  *
       3                 :            :  * Copyright © 2018 Endless Mobile, Inc.
       4                 :            :  *
       5                 :            :  * This library is free software; you can redistribute it and/or
       6                 :            :  * modify it under the terms of the GNU Lesser General Public
       7                 :            :  * License as published by the Free Software Foundation; either
       8                 :            :  * version 2.1 of the License, or (at your option) any later version.
       9                 :            :  *
      10                 :            :  * This library is distributed in the hope that it will be useful,
      11                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13                 :            :  * Lesser General Public License for more details.
      14                 :            :  *
      15                 :            :  * You should have received a copy of the GNU Lesser General Public
      16                 :            :  * License along with this library; if not, write to the Free Software
      17                 :            :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
      18                 :            :  *
      19                 :            :  * Authors:
      20                 :            :  *  - Philip Withnall <withnall@endlessm.com>
      21                 :            :  */
      22                 :            : 
      23                 :            : #include "config.h"
      24                 :            : 
      25                 :            : #include <glib/gi18n-lib.h>
      26                 :            : #include <glib.h>
      27                 :            : #include <glib-object.h>
      28                 :            : #include <gio/gio.h>
      29                 :            : #include <libmogwai-tariff/period.h>
      30                 :            : #include <libmogwai-tariff/tariff-builder.h>
      31                 :            : #include <libmogwai-tariff/tariff.h>
      32                 :            : 
      33                 :            : 
      34                 :            : static void mwt_tariff_builder_dispose (GObject *object);
      35                 :            : 
      36                 :            : /**
      37                 :            :  * MwtTariffBuilder:
      38                 :            :  *
      39                 :            :  * A helper object for constructing an #MwtTariff and serialising it to a
      40                 :            :  * #GBytes which can be transmitted or stored. See #MwtTariffLoader for the
      41                 :            :  * inverse operation.
      42                 :            :  *
      43                 :            :  * When using a #MwtTariffBuilder, all the required properties of the tariff
      44                 :            :  * must be set (including at least one period), then
      45                 :            :  * mwt_tariff_builder_get_tariff() can be used to get the resulting #MwtTariff
      46                 :            :  * object (similarly for the variants to return it as bytes or a #GVariant).
      47                 :            :  * Before then, mwt_tariff_builder_get_tariff() will return %NULL.
      48                 :            :  *
      49                 :            :  * A #MwtTariffBuilder may be used multiple times, or an in-progress tariff may
      50                 :            :  * be destroyed by using mwt_tariff_builder_reset().
      51                 :            :  *
      52                 :            :  * Since: 0.1.0
      53                 :            :  */
      54                 :            : struct _MwtTariffBuilder
      55                 :            : {
      56                 :            :   GObject parent;
      57                 :            : 
      58                 :            :   gchar *name;  /* (owned) */
      59                 :            :   GPtrArray *periods;  /* (element-type MwtPeriod) (owned) */
      60                 :            : 
      61                 :            :   MwtTariff *final_tariff;  /* (nullable) (owned) */
      62                 :            :   GVariant *final_variant;  /* (nullable) (owned) */
      63                 :            : };
      64                 :            : 
      65   [ +  +  +  -  :         86 : G_DEFINE_TYPE (MwtTariffBuilder, mwt_tariff_builder, G_TYPE_OBJECT)
                   +  + ]
      66                 :            : 
      67                 :            : static void
      68                 :          5 : mwt_tariff_builder_class_init (MwtTariffBuilderClass *klass)
      69                 :            : {
      70                 :          5 :   GObjectClass *object_class = (GObjectClass *) klass;
      71                 :            : 
      72                 :          5 :   object_class->dispose = mwt_tariff_builder_dispose;
      73                 :          5 : }
      74                 :            : 
      75                 :            : static void
      76                 :          8 : mwt_tariff_builder_init (MwtTariffBuilder *self)
      77                 :            : {
      78                 :          8 :   self->periods = g_ptr_array_new_with_free_func (g_object_unref);
      79                 :          8 : }
      80                 :            : 
      81                 :            : static void
      82                 :          8 : mwt_tariff_builder_dispose (GObject *object)
      83                 :            : {
      84                 :          8 :   MwtTariffBuilder *self = MWT_TARIFF_BUILDER (object);
      85                 :            : 
      86         [ +  + ]:          8 :   g_clear_pointer (&self->name, g_free);
      87         [ +  - ]:          8 :   g_clear_pointer (&self->periods, g_ptr_array_unref);
      88         [ +  + ]:          8 :   g_clear_object (&self->final_tariff);
      89         [ +  + ]:          8 :   g_clear_pointer (&self->final_variant, g_variant_unref);
      90                 :            : 
      91                 :            :   /* Chain up to the parent class */
      92                 :          8 :   G_OBJECT_CLASS (mwt_tariff_builder_parent_class)->dispose (object);
      93                 :          8 : }
      94                 :            : 
      95                 :            : /**
      96                 :            :  * mwt_tariff_builder_new:
      97                 :            :  *
      98                 :            :  * Create a new, empty #MwtTariffBuilder.
      99                 :            :  *
     100                 :            :  * Returns: (transfer full): a new #MwtTariffBuilder
     101                 :            :  * Since: 0.1.0
     102                 :            :  */
     103                 :            : MwtTariffBuilder *
     104                 :          8 : mwt_tariff_builder_new (void)
     105                 :            : {
     106                 :          8 :   return g_object_new (MWT_TYPE_TARIFF_BUILDER, NULL);
     107                 :            : }
     108                 :            : 
     109                 :            : /**
     110                 :            :  * mwt_tariff_builder_reset:
     111                 :            :  * @self: a #MwtTariffBuilder
     112                 :            :  *
     113                 :            :  * Reset the state of the builder, clearing any completed or in-progress
     114                 :            :  * tariffs.
     115                 :            :  *
     116                 :            :  * Since: 0.1.0
     117                 :            :  */
     118                 :            : void
     119                 :          3 : mwt_tariff_builder_reset (MwtTariffBuilder *self)
     120                 :            : {
     121         [ -  + ]:          3 :   g_return_if_fail (MWT_IS_TARIFF_BUILDER (self));
     122                 :            : 
     123         [ +  + ]:          3 :   g_clear_pointer (&self->name, g_free);
     124                 :          3 :   g_ptr_array_set_size (self->periods, 0);
     125         [ -  + ]:          3 :   g_clear_object (&self->final_tariff);
     126         [ -  + ]:          3 :   g_clear_pointer (&self->final_variant, g_variant_unref);
     127                 :            : }
     128                 :            : 
     129                 :            : /**
     130                 :            :  * mwt_tariff_builder_set_name:
     131                 :            :  * @self: a #MwtTariffBuilder
     132                 :            :  * @name: name for the new tariff
     133                 :            :  *
     134                 :            :  * Set the name for the tariff under construction. See #MwtTariff:name for
     135                 :            :  * details of valid names.
     136                 :            :  *
     137                 :            :  * Since: 0.1.0
     138                 :            :  */
     139                 :            : void
     140                 :          7 : mwt_tariff_builder_set_name (MwtTariffBuilder *self,
     141                 :            :                              const gchar      *name)
     142                 :            : {
     143         [ -  + ]:          7 :   g_return_if_fail (MWT_IS_TARIFF_BUILDER (self));
     144         [ -  + ]:          7 :   g_return_if_fail (mwt_tariff_validate_name (name));
     145                 :            : 
     146                 :          7 :   g_free (self->name);
     147                 :          7 :   self->name = g_strdup (name);
     148                 :            : }
     149                 :            : 
     150                 :            : /**
     151                 :            :  * mwt_tariff_builder_add_period:
     152                 :            :  * @self: a #MwtTariffBuilder
     153                 :            :  * @period: (transfer none): a #MwtPeriod to add to the tariff
     154                 :            :  *
     155                 :            :  * Add the given #MwtPeriod to the tariff under construction. This may be called
     156                 :            :  * multiple times for a given tariff, and must be called at least once per valid
     157                 :            :  * tariff.
     158                 :            :  *
     159                 :            :  * Periods may be added in any order; they will be sorted before the tariff is
     160                 :            :  * generated.
     161                 :            :  *
     162                 :            :  * Since: 0.1.0
     163                 :            :  */
     164                 :            : void
     165                 :          9 : mwt_tariff_builder_add_period (MwtTariffBuilder *self,
     166                 :            :                                MwtPeriod        *period)
     167                 :            : {
     168         [ -  + ]:          9 :   g_return_if_fail (MWT_IS_TARIFF_BUILDER (self));
     169         [ -  + ]:          9 :   g_return_if_fail (MWT_IS_PERIOD (period));
     170                 :            : 
     171                 :          9 :   g_ptr_array_add (self->periods, g_object_ref (period));
     172                 :            : }
     173                 :            : 
     174                 :            : /* Order by decreasing span, then by increasing start date/time. */
     175                 :            : static gint
     176                 :          3 : periods_sort_cb (gconstpointer a,
     177                 :            :                  gconstpointer b)
     178                 :            : {
     179                 :          3 :   MwtPeriod *p1 = *((MwtPeriod **) a);
     180                 :          3 :   MwtPeriod *p2 = *((MwtPeriod **) b);
     181                 :            : 
     182                 :          3 :   GDateTime *p1_start = mwt_period_get_start (p1);
     183                 :          3 :   GDateTime *p1_end = mwt_period_get_end (p2);
     184                 :          3 :   GDateTime *p2_start = mwt_period_get_start (p1);
     185                 :          3 :   GDateTime *p2_end = mwt_period_get_end (p2);
     186                 :            : 
     187                 :          3 :   GTimeSpan p1_span = g_date_time_difference (p1_end, p1_start);
     188                 :          3 :   GTimeSpan p2_span = g_date_time_difference (p2_end, p2_start);
     189                 :            : 
     190         [ +  - ]:          3 :   if (p1_span == p2_span)
     191                 :          3 :     return g_date_time_compare (p1_start, p2_start);
     192                 :            : 
     193         [ #  # ]:          0 :   return (p1_span > p2_span) ? -1 : 1;
     194                 :            : }
     195                 :            : 
     196                 :            : /**
     197                 :            :  * mwt_tariff_builder_get_tariff:
     198                 :            :  * @self: a #MwtTariffBuilder
     199                 :            :  *
     200                 :            :  * Get the newly constructed #MwtTariff, or %NULL if the builder is incomplete,
     201                 :            :  * has been reset, or if there was an error building the tariff. The tariff can
     202                 :            :  * be retrieved multiple times from this function; the builder is not reset
     203                 :            :  * after this function is called.
     204                 :            :  *
     205                 :            :  * Returns: (transfer full) (nullable): the constructed #MwtTariff, or %NULL if
     206                 :            :  *    one is not currently constructed
     207                 :            :  * Since: 0.1.0
     208                 :            :  */
     209                 :            : MwtTariff *
     210                 :         19 : mwt_tariff_builder_get_tariff (MwtTariffBuilder *self)
     211                 :            : {
     212                 :         19 :   g_autoptr(GError) local_error = NULL;
     213                 :            : 
     214         [ -  + ]:         19 :   g_return_val_if_fail (MWT_IS_TARIFF_BUILDER (self), NULL);
     215                 :            : 
     216                 :            :   /* If we haven’t constructed the final tariff yet, try. If it fails, just
     217                 :            :    * return %NULL since either the builder is unused, or the developer has made
     218                 :            :    * an error. */
     219         [ +  + ]:         19 :   if (self->final_tariff == NULL)
     220                 :            :     {
     221                 :            :       /* Ensure the periods are in order. */
     222                 :         17 :       g_ptr_array_sort (self->periods, periods_sort_cb);
     223                 :            : 
     224         [ +  + ]:         17 :       if (!mwt_tariff_validate (self->name, self->periods, &local_error))
     225                 :            :         {
     226                 :         12 :           g_debug ("Invalid tariff: %s", local_error->message);
     227                 :         12 :           return NULL;
     228                 :            :         }
     229                 :          5 :       self->final_tariff = mwt_tariff_new (self->name, self->periods);
     230                 :            :     }
     231                 :            : 
     232                 :          7 :   return g_object_ref (self->final_tariff);
     233                 :            : }
     234                 :            : 
     235                 :            : /* Returns a new floating variant. */
     236                 :            : static GVariant *
     237                 :          5 : mwt_tariff_builder_build_tariff_variant (const gchar *name,
     238                 :            :                                          MwtTariff   *tariff)
     239                 :            : {
     240                 :          5 :   const guint16 format_version = 2;
     241                 :          5 :   const gchar *format_magic = "Mogwai tariff";
     242                 :         10 :   g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(sa(ttssqut))"));
     243                 :            : 
     244                 :          5 :   g_variant_builder_add (&builder, "s", name);
     245                 :            : 
     246                 :            :   /* Periods. mwt_tariff_get_periods() guarantees it’s in order. */
     247                 :          5 :   GPtrArray *periods = mwt_tariff_get_periods (tariff);
     248                 :          5 :   g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(ttssqut)"));
     249                 :            : 
     250         [ +  + ]:         13 :   for (gsize i = 0; i < periods->len; i++)
     251                 :            :     {
     252                 :          8 :       MwtPeriod *period = g_ptr_array_index (periods, i);
     253                 :          8 :       GDateTime *start = mwt_period_get_start (period);
     254                 :          8 :       GTimeZone *start_tz = g_date_time_get_timezone (start);
     255                 :          8 :       GDateTime *end = mwt_period_get_end (period);
     256                 :          8 :       GTimeZone *end_tz = g_date_time_get_timezone (end);
     257                 :          8 :       guint64 start_unix = g_date_time_to_unix (start);
     258                 :          8 :       guint64 end_unix = g_date_time_to_unix (end);
     259                 :            : 
     260                 :          8 :       g_variant_builder_open (&builder, G_VARIANT_TYPE ("(ttssqut)"));
     261                 :          8 :       g_variant_builder_add (&builder, "t", start_unix);
     262                 :          8 :       g_variant_builder_add (&builder, "t", end_unix);
     263                 :          8 :       g_variant_builder_add (&builder, "s", g_time_zone_get_identifier (start_tz));
     264                 :          8 :       g_variant_builder_add (&builder, "s", g_time_zone_get_identifier (end_tz));
     265                 :          8 :       g_variant_builder_add (&builder, "q", (guint16) mwt_period_get_repeat_type (period));
     266                 :          8 :       g_variant_builder_add (&builder, "u", (guint32) mwt_period_get_repeat_period (period));
     267                 :          8 :       g_variant_builder_add (&builder, "t", mwt_period_get_capacity_limit (period));
     268                 :          8 :       g_variant_builder_close (&builder);
     269                 :            :     }
     270                 :            : 
     271                 :          5 :   g_variant_builder_close (&builder);
     272                 :            : 
     273                 :            :   /* Add the file format version, which also acts as a byte order mark (so
     274                 :            :    * explicitly don’t convert it to a known endianness). The magic bytes allow
     275                 :            :    * content type detection. */
     276                 :          5 :   return g_variant_new ("(sqv)",
     277                 :            :                         format_magic, format_version,
     278                 :            :                         g_variant_builder_end (&builder));
     279                 :            : }
     280                 :            : 
     281                 :            : /**
     282                 :            :  * mwt_tariff_builder_get_tariff_as_variant:
     283                 :            :  * @self: a #MwtTariffBuilder
     284                 :            :  *
     285                 :            :  * Get the newly constructed tariff as a #GVariant. This will return %NULL in
     286                 :            :  * exactly the same situations as when mwt_tariff_builder_get_tariff() returns
     287                 :            :  * %NULL.
     288                 :            :  *
     289                 :            :  * The returned #GVariant is guaranteed to be in normal form, and a non-floating
     290                 :            :  * reference will be returned.
     291                 :            :  *
     292                 :            :  * Returns: (transfer full) (nullable): a new, non-floating ref to the
     293                 :            :  *    constructed tariff as a #GVariant, or %NULL if one is not currently
     294                 :            :  *    constructed
     295                 :            :  * Since: 0.1.0
     296                 :            :  */
     297                 :            : GVariant *
     298                 :         14 : mwt_tariff_builder_get_tariff_as_variant (MwtTariffBuilder *self)
     299                 :            : {
     300         [ -  + ]:         14 :   g_return_val_if_fail (MWT_IS_TARIFF_BUILDER (self), NULL);
     301                 :            : 
     302         [ +  + ]:         14 :   if (self->final_variant == NULL)
     303                 :            :     {
     304         [ +  + ]:         26 :       g_autoptr(MwtTariff) tariff = mwt_tariff_builder_get_tariff (self);
     305         [ +  + ]:         13 :       if (tariff == NULL)
     306                 :          8 :         return NULL;
     307                 :            : 
     308                 :          5 :       g_autoptr(GVariant) variant = mwt_tariff_builder_build_tariff_variant (self->name, tariff);
     309                 :          5 :       self->final_variant = g_variant_ref_sink (g_steal_pointer (&variant));
     310                 :            :     }
     311                 :            : 
     312                 :          6 :   return g_variant_ref (self->final_variant);
     313                 :            : }
     314                 :            : 
     315                 :            : /**
     316                 :            :  * mwt_tariff_builder_get_tariff_as_bytes:
     317                 :            :  * @self: a #MwtTariffBuilder
     318                 :            :  *
     319                 :            :  * Get the newly constructed tariff as a #GBytes. This will return %NULL in
     320                 :            :  * exactly the same situations as when mwt_tariff_builder_get_tariff() returns
     321                 :            :  * %NULL.
     322                 :            :  *
     323                 :            :  * The returned #GBytes is suitable to be written to a file or sent over the
     324                 :            :  * network. Its byte ordering is encoded so it may be loaded on a system with
     325                 :            :  * a different byte ordering.
     326                 :            :  *
     327                 :            :  * Returns: (transfer full) (nullable): the constructed tariff as a #GBytes,
     328                 :            :  *    or %NULL if one is not currently constructed
     329                 :            :  * Since: 0.1.0
     330                 :            :  */
     331                 :            : GBytes *
     332                 :          8 : mwt_tariff_builder_get_tariff_as_bytes (MwtTariffBuilder *self)
     333                 :            : {
     334         [ -  + ]:          8 :   g_return_val_if_fail (MWT_IS_TARIFF_BUILDER (self), NULL);
     335                 :            : 
     336                 :         16 :   g_autoptr(GVariant) variant = mwt_tariff_builder_get_tariff_as_variant (self);
     337         [ +  + ]:          8 :   if (variant == NULL)
     338                 :          4 :     return NULL;
     339                 :            : 
     340                 :          4 :   return g_variant_get_data_as_bytes (variant);
     341                 :            : }

Generated by: LCOV version 1.16