## format.md
class Format

Convert values to a string given a format specification

```raku
class Format { }
```

A `Format` is an immutable object containing the logic for converting a set of values to a string given a `sprintf` compatible format specification. Acts as a standard string in every way, except that it can also be called as a [`Callable`](/type/Callable) with arguments to produce a string, just as `sprintf`.

Available as of the 2023.06 release of the Rakudo compiler. Requires language level `6.e`.

```raku
use v6.e.PREVIEW;
my $f = Format.new("'%5s'");
say $f;                         # OUTPUT: «'%5s'␤»
say $f("foo");                  # OUTPUT: «'  foo'␤»

```

# [Methods](#class_Format "go to top of document")[§](#Methods "direct link")

## [method new](#class_Format "go to top of document")[§](#method_new "direct link")

```raku
method new($format --> Format:D)

```

Creates a new `Format` object from a `sprintf` compatible format string.

```raku
use v6.e.PREVIEW;
my $d = Format.new("%05d");
say $d;                         # OUTPUT: «%05d␤»
say $d(42);                     # OUTPUT: «00042␤»

```

## [method Callable](#class_Format "go to top of document")[§](#method_Callable "direct link")

```raku
method Callable(--> Callable:D)
```

Returns the [`Callable`](/type/Callable) that was created from the given format. Intended for introspection purposes only, as one can call the `Format` object directly.

## [method directives](#class_Format "go to top of document")[§](#method_directives "direct link")

```raku
method directives(--> List:D)
```

Returns a list of the directives seen in the given format. Intended for introspection purposes.

```raku
use v6.e.PREVIEW;
my $d = Format.new("%05d%3x:%s");
say $d.directives;              # OUTPUT: «(d x s)␤»

```

## [method arity](#class_Format "go to top of document")[§](#method_arity "direct link")

```raku
method arity(--> List:D)
```

Returns the **minimal** number of positional arguments that is needed for this format. Intended for introspection purposes.

```raku
use v6.e.PREVIEW;
my $d = Format.new("%05d%3x:%s");
say $d.arity;                   # OUTPUT: «3␤»

```

## [method count](#class_Format "go to top of document")[§](#method_count "direct link")

```raku
method count(--> List:D)
```

Returns the **maximal** number of positional arguments that is needed for this format. Intended for introspection purposes.

```raku
use v6.e.PREVIEW;
my $d = Format.new("%05d%3x:%s");
say $d.count;                   # OUTPUT: «3␤»

```


## dist_zef-bduggan-Duckie.md
# NAME

Duckie - A wrapper and native bindings for DuckDB

# SYNOPSIS

```
use Duckie;
Duckie.new.query('select name from META6.json').rows[0]
# {name => Duckie}

my $db = Duckie.new;
say $db.query("select 1 as the_loneliest_number").column-data(0);
# [1]

with $db.query("select 1 as the_loneliest_number") -> $result {
  say $result.column-data('the_loneliest_number'); # [1]
} else {
  # Errors are soft failures
  say "Failed to run query: $_";
}

# DuckDB can query or import data from CSV or JSON files, HTTP URLs,
# PostgreSQL, MySQL, SQLite databases and more.
my @cols = $db.query('select * from data.csv').columns;
my @rows = $db.query('select * from data.json').rows;

$db.query: q[ attach 'postgres://secret:pw@localhost/dbname' as pg (type postgres)]
$res = $db.query: "select * from pg.my_table"

$db.query("install httpfs");
$db.query("load httpfs");
$res = $db.query: "select * from 'http://example.com/data.csv'";

# Joins between different types are also possible.
$res = $db.query: q:to/SQL/
select *
from pg.my_table one
inner join 'http://example.com/data.csv' csv_data on one.id = csv_data.id
inner join 'data.json' json_data on one.id = json_data.id
SQL

```

# DESCRIPTION

This module provides Raku bindings for [DuckDB](https://duckdb.org/). DuckDB is a "fast in-process analytical database". It provides an SQL interface for a variety of data sources. Result sets are column-oriented, with a rich set of types that are either inferred, preserved, or explicitly defined. `Duckie` also provides a row-oriented API.

This module provides two sets of classes.

* `Duckie::DuckDB::Native` is a low-level interface that directly maps to the [C API](https://duckdb.org/docs/api/c/api.html). Note that a number of the function calls there are either deprecated or scheduled for deprecation, so the implementation of the Raku interface favors the more recent mechanisms where possible.
* `Duckie` provides a high level interface that handles things like memory management and native typecasting. While the Raku language supports native types, the results from `Duckie` do not currently expose them, preferring, for instance to return Integers instead of uint8s, int64s, etc, and using Rats for decimals, and Nums for floats. A future interface may expose native types.

# EXPORTS

If an argument to `use Duckie` is provided, a new `Duckie` object is created and returned. Also "-debug" will enable debug output. e.g.

```
use Duckie;                 # no exports
use Duckie '$db';           # creates and exports "$db"
use Duckie '$db', '-debug'; # creates and exports "$db" with debug output

use Duckie 'db';
db.query("select 1 as the_loneliest_number").column-data(0);

```

# METHODS

### method new

```
method new(
  :$file = ':memory:'
) returns Duckie

```

Create a new Duckie object. The optional `:file` parameter specifies the path to a file to use as a database. If not specified, an in-memory database is used. The database is opened and connected to when the object is created.

### method query

```
method query(
    Str $sql
) returns Duckie::Result

```

Run a query and return a result. If the query fails, a soft failure is thrown.

### method DESTROY

```
method DESTROY() returns Mu

```

Close the database connection and free resources.

# SEE ALSO

* [Duckie::Result](https://github.com/bduggan/raku-duckie/blob/main/docs/lib/Duckie/Result.md)
* [Duckie::DuckDB::Native](https://github.com/bduggan/raku-duckie/blob/main/docs/lib/Duckie/DuckDB/Native.md)

# ENVIRONMENT

Set `DUCKIE_DEBUG` to a true value to enable logging to `STDERR`.

# AUTHOR

Brian Duggan


## mop.md
Metaobject protocol (MOP)

Introspection and the Raku object system

Raku is built on a metaobject layer. That means that there are objects (the *metaobjects*) that control how various object-oriented constructs (such as classes, roles, methods, attributes or enums) behave.

The metaobject has a practical benefit to the user when a normal object's type is needed. For example:

```raku
my $arr = [1, 2];
say $arr.^name;   # OUTPUT: «Array␤»

```

To get a more in-depth understanding of the metaobject for a `class`, here is an example repeated twice: once as normal declarations in Raku, and once expressed through the [metamodel](/type/Metamodel/ClassHOW):

```raku
class A {
    method x() { say 42 }
}

A.x();
```

corresponds to:

```raku
constant A := Metamodel::ClassHOW.new_type( name => 'A' );  # class A {
A.^add_method('x', my method x(A:) { say 42 });             #   method x()
A.^compose;                                                 # }

A.x();
```

(except that the declarative form is executed at compile time, and the latter form does not).

The metaobject behind an object can be obtained with `$obj.HOW`, where HOW stands for Higher Order Workings (or, *HOW the \*%@$ does this work?*).

Here, the calls with `.^` are calls to the metaobject, so `A.^compose` is a shortcut for `A.HOW.compose(A)`. The invocant is passed in the parameter list as well, to make it possible to support prototype-style type systems, where there is just one metaobject (and not one metaobject per type, as standard Raku does it).

As the example above demonstrates, all object oriented features are available to the user, not just to the compiler. In fact the compiler just uses such calls to metaobjects.

# [Metamethods](#Metaobject_protocol_(MOP) "go to top of document")[§](#Metamethods "direct link")

These are introspective macros that resemble method calls.

Metamethods are generally named with ALLCAPS, and it is considered good style to avoid creating your own methods with ALLCAPS names (since they are used conventionally for things like [phasers](/syntax/Phasers). This will avoid conflicts with any metamethods that may appear in future versions of the language.

## [WHAT](#Metaobject_protocol_(MOP) "go to top of document")[§](#WHAT "direct link")

The type object of the type. This is a pseudo-method that can be overloaded without producing error or warning, but will be ignored.

For example `42.WHAT` returns the [`Int`](/type/Int) type object.

## [WHICH](#Metaobject_protocol_(MOP) "go to top of document")[§](#WHICH "direct link")

The object's identity value. This can be used for hashing and identity comparison, and is how the `===` infix operator is implemented.

## [WHO](#Metaobject_protocol_(MOP) "go to top of document")[§](#WHO "direct link")

The package supporting the object.

## [WHERE](#Metaobject_protocol_(MOP) "go to top of document")[§](#WHERE "direct link")

The memory address of the object. Note that this is not stable in implementations with moving/compacting garbage collectors. Use `WHICH` for a stable identity indicator.

## [HOW](#Metaobject_protocol_(MOP) "go to top of document")[§](#HOW "direct link")

Returns the metaclass object, as in "Higher Order Workings".

```raku
say (%).HOW.^name # OUTPUT: «Perl6::Metamodel::ClassHOW+{<anon>}␤»
```

`HOW` returns an object of type `Perl6::Metamodel::ClassHOW` in this case; objects of this type are used to build classes. The same operation on the `&` sigil will return `Perl6::Metamodel::ParametricRoleGroupHOW`. You will be calling this object whenever you use the `^` syntax to access metamethods. In fact, the code above is equivalent to `say (&).HOW.HOW.name(&)` which is much more unwieldy. [`Metamodel::ClassHOW`](/type/Metamodel/ClassHOW) is part of the Rakudo implementation, so use with caution.

## [WHY](#Metaobject_protocol_(MOP) "go to top of document")[§](#WHY "direct link")

The attached [Pod](/language/pod) value.

## [DEFINITE](#Metaobject_protocol_(MOP) "go to top of document")[§](#DEFINITE "direct link")

The object has a valid concrete representation. This is a pseudo-method that can be overloaded without producing error or warning, but will be ignored.

Returns `True` for instances and `False` for type objects.

## [VAR](#Metaobject_protocol_(MOP) "go to top of document")[§](#VAR "direct link")

Returns the underlying [`Scalar`](/type/Scalar) object, if there is one.

The presence of a [`Scalar`](/type/Scalar) object indicates that the object is "itemized".

```raku
.say for (1, 2, 3);           # OUTPUT: «1␤2␤3␤», not itemized
.say for $(1, 2, 3);          # OUTPUT: «(1 2 3)␤», itemized
say (1, 2, 3).VAR ~~ Scalar;  # OUTPUT: «False␤»
say $(1, 2, 3).VAR ~~ Scalar; # OUTPUT: «True␤»
```

Please refer to the [section on item context](/language/contexts#Item_context) for more information.

# [Metaclass methods](#Metaobject_protocol_(MOP) "go to top of document")[§](#Metaclass_methods "direct link")

Same as you can define object and class methods (which do not have access to the instance variables), you can define metaclass methods, which will work on the metaclass. These are conventionally defined by a caret (`^`) at the front of the method identifier. These metaclass methods might return a type object or a simple object; in general, they are only conventionally related to the metaobject protocol and are, otherwise, simple methods with a peculiar syntax.

These methods will get called with the type name as first argument, but this needs to be declared explicitly.

```raku
class Foo {
    method ^bar( Mu \foo) {
        foo.^set_name( foo.^name ~ "[þ]" );
    }
}
my $foo = Foo.new();
say $foo.^name; # OUTPUT: «Foo␤»
Foo.^bar();
say $foo.^name; # OUTPUT: «Foo[þ]␤»

```

This metaclass method will, via invoking class metamethods, change the name of the class it's been declared. Since this has been acting on the metaclass, any new object of the same class will receive the same name; invoking `say Foo .new().^name` will return the same value. As it can be seen, the metaclass method is invoked with no arguments; `\foo` will, in this case, become the `Foo` when invoked.

The metaclass methods can receive as many arguments as you want.

```raku
class Foo {
    method ^bar( Mu \foo, Str $addenda) {
        foo.^set_name( foo.^name ~ $addenda );
    }
}
Foo.new().^bar(  "[baz]" );
my $foo = Foo.new();
say $foo.^name;  # OUTPUT: «Foo[baz]␤»

```

Again, implicitly, the method call will furnish the first argument, which is the type object. Since they are metaclass methods, you can invoke them on a class (as above) or on an object (as below). The result will be exactly the same.

# [Structure of the metaobject system](#Metaobject_protocol_(MOP) "go to top of document")[§](#Structure_of_the_metaobject_system "direct link")

**Note:** this documentation largely reflects the metaobject system as implemented by the [Rakudo Raku compiler](https://rakudo.org/), since the [design documents](https://design.raku.org/) are very light on details.

For each type declarator keyword, such as `class`, `role`, `enum`, `module`, `package`, `grammar` or `subset`, there is a separate metaclass in the `Metamodel::` namespace. (Rakudo implements them in the `Perl6::Metamodel::` namespace, and then maps `Perl6::Metamodel` to `Metamodel`).

Many of these metaclasses share common functionality. For example roles, grammars and classes can all contain methods and attributes, as well as being able to do roles. This shared functionality is implemented in roles which are composed into the appropriate metaclasses. For example, the role [`Metamodel::RoleContainer`](/type/Metamodel/RoleContainer) implements the functionality that a type can hold roles and [`Metamodel::ClassHOW`](/type/Metamodel/ClassHOW), which is the metaclass behind the `class` keyword, does this role.

Most metaclasses have a `compose` method that you must call when you're done creating or modifying a metaobject. It creates method caches, validates things and so on, and weird behavior ensues if you forget to call it, so don't :-).

## [Bootstrapping concerns](#Metaobject_protocol_(MOP) "go to top of document")[§](#Bootstrapping_concerns "direct link")

You might wonder how [`Metamodel::ClassHOW`](/type/Metamodel/ClassHOW) can be a class, when being a class is defined in terms of [`Metamodel::ClassHOW`](/type/Metamodel/ClassHOW), or how the roles responsible for role handling can be roles. The answer is *by magic*.

Just kidding. Bootstrapping is implementation specific. Rakudo does it by using the object system of the language in which itself is implemented, which happens to be (nearly) a subset of Raku known as [NQP](/language/faq#What_language_is_NQP_written_in?). NQP has a primitive, class-like kind called `knowhow`, which is used to bootstrap its own classes and roles implementation. `knowhow` is built on primitives that the virtual machine under NQP provides.

Since the object model is bootstrapped in terms of lower-level types, introspection can sometimes return low-level types instead of the ones you expect, like an NQP-level routine instead of a normal [`Routine`](/type/Routine) object, or a bootstrap-attribute instead of [`Attribute`](/type/Attribute).

## [Composition time and static reasoning](#Metaobject_protocol_(MOP) "go to top of document")[§](#Composition_time_and_static_reasoning "direct link")

In Raku, a type is constructed as it is parsed, so in the beginning, it must be mutable. However if all types were always mutable, all reasoning about them would get invalidated at any modification of a type. For example the list of parent types and thus the result of type checking can change during that time.

So to get the best of both worlds, there is a time when a type transitions from mutable to immutable. This is called *composition*, and for syntactically declared types, it happens when the type declaration is fully parsed (so usually when the closing curly brace is parsed).

If you create types through the metaobject system directly, you must call `.^compose` on them before they become fully functional.

Most metaclasses also use composition time to calculate some properties like the method resolution order, publish a method cache, and other house-keeping tasks. Meddling with types after they have been composed is sometimes possible, but usually a recipe for disaster. Don't do it.

## [Power and responsibility](#Metaobject_protocol_(MOP) "go to top of document")[§](#Power_and_responsibility "direct link")

The metaobject protocol offers much power that regular Raku code intentionally limits, such as calling private methods on classes that don't trust you, peeking into private attributes, and other things that usually simply aren't done.

Regular Raku code has many safety checks in place; not so the metamodel. It is close to the underlying virtual machine, and violating the contracts with the VM can lead to all sorts of strange behaviors that, in normal code, would obviously be bugs.

So be extra careful and thoughtful when writing metatypes.

## [Power, convenience and pitfalls](#Metaobject_protocol_(MOP) "go to top of document")[§](#Power,_convenience_and_pitfalls "direct link")

The metaobject protocol is designed to be powerful enough to implement the Raku object system. This power occasionally comes at the cost of convenience.

For example, when you write `my $x = 42` and then proceed to call methods on `$x`, most of these methods end up acting on the [integer](/type/Int) 42, not on the [scalar container](/type/Scalar) in which it is stored. This is a piece of convenience found in ordinary Raku. Many parts of the metaobject protocol cannot afford to offer the convenience of automatically ignoring scalar containers, because they are used to implement those scalar containers as well. So if you write `my $t = MyType; ... ; $t.^compose` you are composing the Scalar that the `$`-sigiled variable implies, not `MyType`.

The consequence is that you need to have a rather detailed understanding of the subtleties of Raku in order to avoid pitfalls when working with the MOP, and can't expect the same ["do what I mean"](/language/glossary#DWIM) convenience that ordinary Raku code offers.

# [Archetypes](#Metaobject_protocol_(MOP) "go to top of document")[§](#Archetypes "direct link")

Typically, when multiple kinds of types share a property, it is implemented with a metarole to be mixed into their metaclasses. However, not all common properties of types can be implemented as mixins. Certain properties are common to various kinds of types, but do not share enough behavior to be possible to implement as mixins. These properties are known as *archetypes*.

HOWs should provide an `archetypes` metamethod that takes no arguments and returns a `Metamodel::Archetypes` instance. This is used by the compiler to determine what archetypes are supported by metaobjects. The rest of this section will cover how each of the archetypes that exist in Rakudo work.

## [parametric](#Metaobject_protocol_(MOP) "go to top of document")[§](#parametric "direct link")

Parametric types are incomplete types that may have an arbitrary number of type parameters. Here, type parameters refer to parameters of the type itself; these may be any object that a signature allows you to include, not just types alone. When parameterized with type arguments, parametric types will produce a more complete type of some sort.

If a HOW supports parameterization, it should have the `parametric` archetype and must provide a `parameterize` metamethod. The `parameterize` metamethod must accept a metaobject and may accept any number of type parameters as arguments, returning a metaobject. For example, a `parameterize` metamethod that allows a type to be parameterized with any type arguments may have this signature:

```raku
method parameterize(Mu $obj is raw, |parameters --> Mu)
```

### [Parametric classes and grammars](#Metaobject_protocol_(MOP) "go to top of document")[§](#Parametric_classes_and_grammars "direct link")

Because of how the parametric archetype is implemented, it's possible for classes and grammars to be augmented with support for parameterization by giving them a `parameterize` metamethod, despite the type not having the `parametric` archetype. This can be useful in cases where the features of roles make them inappropriate to use for a parametric type.

One scenario where parametric classes and grammars are useful is when parameterizations of a type should override or add multiple dispatch candidates to existing methods or regexes on the original parametric type. This is the case with parameterizations of types like [`Array`](/type/Array) and [`Hash`](/type/Hash), which may optionally be parameterized to mix in more type-safe versions of their methods that work with instances' values directly. In Rakudo, these are implemented as parametric classes using the metamethods provided by [`Metamodel::Mixins`](/type/Metamodel/Mixins) and [`Metamodel::Naming`](/type/Metamodel/Naming) to create a mixin of the metaobject given and reset its name before returning it. This technique can be used to write extensible grammars when used in combination with multi tokens, for instance:

```raku
grammar Bot::Grammar {
    token TOP { <topic> || .+ }

    proto token topic {*}
    multi token topic:sym<command> { <command> <.ws> <command-args> }

    token command      { '$' <!ws>+ }
    token command-args { <!ws>+ % <.ws> }

    method ^parameterize(::?CLASS:U $this is raw, +roles) {
        my Str:D $name   = self.name: $this;
        my Mu    $mixin := $this.^mixin: |roles;
        $mixin.^set_name: [~] $name, '[', roles.map(*.^name).join(','), ']';
        $mixin
    }
}

role Greetings[Str:D $name] {
    multi token topic:sym<greeting> { ^ [ 'hi' | 'hello' | 'hey' | 'sup' ] <.ws> $name }
}

my constant GreetBot = Bot::Grammar[Greetings['GreetBot']];
GreetBot.parse: 'sup GreetBot';
say ~$/; # OUTPUT: «sup GreetBot␤»

```

Parametric classes can also be used to simulate support for parameterization on other kinds. For instance, the [Failable](https://modules.raku.org/dist/Failable:cpan:KAIEPI) ecosystem module is a parametric class that produces a subset upon parameterization. While the module itself does some caching to ensure no more type objects are made than what's necessary, a more basic version of it can be implemented like so:

```raku
class Failable {
    method ^parameterize(Failable:U $this is raw, Mu $obj is raw --> Mu) {
        Metamodel::SubsetHOW.new_type:
            name       => $this.^name ~ '[' ~ $obj.^name ~ ']',
            refinee    => Metamodel::Primitives.is_type($obj, Any) ?? Any !! Mu,
            refinement => $obj | Failure
    }
}

```


## dist_cpan-CTILMES-LibCurl.md
# Raku LibCurl

[![Build Status](https://travis-ci.org/CurtTilmes/raku-libcurl.svg)](https://travis-ci.org/CurtTilmes/raku-libcurl)

[Simple Examples](#simple-examples)
[Options](#options)
[Header Options](#header-options)
[Special Options](#special-options)
[Errors](#errors)
[Info](#info)
[Received headers](#received-header-fields)
[Content](#content)
[Proxies](#proxies)
[Multi](#multi)

A [Raku](https://raku.org/) interface to
[libcurl](https://curl.haxx.se/libcurl/).

```
libcurl is a free and easy-to-use client-side URL transfer
library, supporting DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS,
IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP,
SMTP, SMTPS, Telnet and TFTP. libcurl supports SSL certificates,
HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload,
proxies, cookies, user+password authentication (Basic, Digest,
NTLM, Negotiate, Kerberos), file transfer resume, http proxy
tunneling and more!

libcurl is highly portable, it builds and works identically on
numerous platforms, including Solaris, NetBSD, FreeBSD, OpenBSD,
Darwin, HPUX, IRIX, AIX, Tru64, Linux, UnixWare, HURD, Windows,
Amiga, OS/2, BeOs, Mac OS X, Ultrix, QNX, OpenVMS, RISC OS, Novell
NetWare, DOS and more...

libcurl is free, thread-safe, IPv6 compatible, feature rich, well
supported, fast, thoroughly documented and is already used by many
known, big and successful companies and numerous applications.

```

## Simple Examples

```
use LibCurl::Easy;

# GET
print LibCurl::Easy.new(URL => 'http://example.com').perform.content;

# GET (download a file)
LibCurl::Easy.new(URL => 'http://example.com/somefile',
                  download => 'somefile').perform;

# HEAD
say LibCurl::Easy.new(:nobody, URL => 'http://example.com')
    .perform.response-code;

# PUT (upload a file)
LibCurl::Easy.new(URL => 'http://example.com/somefile',
                  upload => 'somefile').perform;

# PUT (content from a string)
LibCurl::Easy.new(URL => 'http://example.com/somefile',
                  send => 'My Content').perform;

 # DELETE
LibCurl::Easy.new(URL => 'http://example.com/file-to-delete',
                  customrequest => 'DELETE').perform;

# POST
LibCurl::Easy.new(URL => 'http://example.com/form.html',
                  postfields => 'name=foo&opt=value').perform;

```

## LibCurl::HTTP

If even those aren't easy enough, there is a tiny sub-class
**LibCurl::HTTP** that adds aliases for the common HTTP methods:

```
use LibCurl::HTTP;

my $http = LibCurl::HTTP.new;

say $http.GET('http://example.com').perform.content;

say $http.GET('http://example.com', 'myfile').perform.response-code;

say $http.HEAD('http://example.com').perform.response-code;

$http.DELETE('http://example.com').perform;

$http.PUT('http://example.com', 'myfile').perform;

$http.POST('http://example.com/form.html', 'name=foo&opt=value').perform;

```

LibCurl::HTTP methods also enable `failonerror` by default, so any
HTTP response >= 400 will throw an error.

You can even import very simple subroutines ala [WWW](https://github.com/zoffixznet/perl6-WWW):

```
use LibCurl::HTTP :subs;

# Just GET content (will return Failure on failure):
say get 'https://httpbin.org/get?foo=42&bar=x';

# GET and decode received data as JSON:
say jget('https://httpbin.org/get?foo=42&bar=x')<args><foo>;

# POST content (query args are OK; pass form as named args)
say post 'https://httpbin.org/post?foo=42&bar=x', :some<form>, :42args;

# And if you need headers, pass them inside a positional Hash:
say post 'https://httpbin.org/post?foo=42&bar=x', %(:Some<Custom-Header>),
    :some<form>, :42args;

# Same POST as above + decode response as JSON
say jpost('https://httpbin.org/post', :some<form-arg>)<form><some>;

```

## Fancier Example

```
my $curl = LibCurl::Easy.new(:verbose, :followlocation);
$curl.setopt(URL => 'http://example.com', download => './myfile.html');
$curl.perform;
say $curl.success;
say $curl.Content-Type;
say $curl.Content-Length;
say $curl.Date;
say $curl.response-code;
say $curl.statusline;

```

Of course the full power of [libcurl](https://curl.haxx.se/libcurl) is
available, so you aren't limited to HTTP URLs, you can use FTP, SMTP,
TFTP, SCP, SFTP, and many, many more.

## Options

Many of the libcurl
[options](https://curl.haxx.se/libcurl/c/curl_easy_setopt.html) are
available, mostly just skip the `CURLOPT_`, lowercase, and use '-'
instead of '\_'. For example, instead of
`CURLOPT_ACCEPT_ENCODING`, use `accept-encoding`. When the
options are really boolean (set to 1 to enable and 0 to disable), you
can treat them like option booleans if you want, `:option` to
enable, and `:!option` to disable.

Just like libcurl, the primary option is
[URL](https://curl.haxx.se/libcurl/c/CURLOPT_URL.html), and can take
many forms depending on the desired protocol.

Options can be set on a handle either on initial creation with
`new()`, or later with `.setopt()`. As a convenience, you can
also just treat them like normal object methods. These are all
equivalent:

```
my $curl = LibCurl::Easy.new(:verbose, :followlocation,
                             URL => 'http://example.com');

$curl.setopt(:verbose, followlocation => 1);
$curl.setopt(URL => 'http://example.com');

$curl.verbose(1);
$curl.followlocation(1);
$curl.URL('http://example.com');

```

`postfields` is actually
[CURLOPT\_COPYPOSTFIELDS](https://curl.haxx.se/libcurl/c/CURLOPT_COPYPOSTFIELDS.html) so it will always copy the fields.

Some of the normal options have **\_LARGE** versions. LibCurl always
maps the option to the **\_LARGE** option where they exist, so you
don't have to worry about overflows.

These are the current options (If you want one not in this list, let
me know):

[CAinfo](https://curl.haxx.se/libcurl/c/CURLOPT_CAINFO.html)
[CApath](https://curl.haxx.se/libcurl/c/CURLOPT_CAPATH.html)
[URL](https://curl.haxx.se/libcurl/c/CURLOPT_URL.html)
[accepttimeout-ms](https://curl.haxx.se/libcurl/c/CURLOPT_ACCEPTTIMEOUT_MS.html)
[accept-encoding](https://curl.haxx.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html)
[address-scope](https://curl.haxx.se/libcurl/c/CURLOPT_ADDRESS_SCOPE.html)
[append](https://curl.haxx.se/libcurl/c/CURLOPT_APPEND.html)
[autoreferer](https://curl.haxx.se/libcurl/c/CURLOPT_AUTOREFERER.html)
[buffersize](https://curl.haxx.se/libcurl/c/CURLOPT_BUFFERSIZE.html)
[certinfo](https://curl.haxx.se/libcurl/c/CURLOPT_CERTINFO.html)
[cookie](https://curl.haxx.se/libcurl/c/CURLOPT_COOKIE.html)
[cookiefile](https://curl.haxx.se/libcurl/c/CURLOPT_COOKIEFILE.html)
[cookiejar](https://curl.haxx.se/libcurl/c/CURLOPT_COOKIEJAR.html)
[cookielist](https://curl.haxx.se/libcurl/c/CURLOPT_COOKIELIST.html)
[customrequest](https://curl.haxx.se/libcurl/c/CURLOPT_CUSTOMREQUEST.html)
[default-protocol](https://curl.haxx.se/libcurl/c/CURLOPT_DEFAULT_PROTOCOL.html)
[dirlistonly](https://curl.haxx.se/libcurl/c/CURLOPT_DIRLISTONLY.html)
[failonerror](https://curl.haxx.se/libcurl/c/CURLOPT_FAILONERROR.html)
[followlocation](https://curl.haxx.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html)
[forbid-reuse](https://curl.haxx.se/libcurl/c/CURLOPT_FORBID_REUSE.html)
[fresh-connect](https://curl.haxx.se/libcurl/c/CURLOPT_FRESH_CONNECT.html)
[ftp-skip-pasv-ip](https://curl.haxx.se/libcurl/c/CURLOPT_FTP_SKIP_PASV_IP.html)
[ftp-use-eprt](https://curl.haxx.se/libcurl/c/CURLOPT_FTP_USE_EPRT.html)
[ftp-use-epsv](https://curl.haxx.se/libcurl/c/CURLOPT_FTP_USE_EPSV.html)
[ftpport](https://curl.haxx.se/libcurl/c/CURLOPT_FTPPORT.html)
[header](https://curl.haxx.se/libcurl/c/CURLOPT_HEADER.html)
[http-version](https://curl.haxx.se/libcurl/c/CURLOPT_HTTP_VERSION.html)
[httpauth](https://curl.haxx.se/libcurl/c/CURLOPT_HTTPAUTH.html)
[httpget](https://curl.haxx.se/libcurl/c/CURLOPT_HTTPGET.html)
[httpproxytunnel](https://curl.haxx.se/libcurl/c/CURLOPT_HTTPPROXYTUNNEL.html)
[ignore-content-length)](https://curl.haxx.se/libcurl/c/CURLOPT_IGNORE_CONTENT_LENGTH.html)
[infilesize](https://curl.haxx.se/libcurl/c/CURLOPT_INFILESIZE_LARGE.html)
[localport](https://curl.haxx.se/libcurl/c/CURLOPT_LOCALPORT.html)
[localportrange](https://curl.haxx.se/libcurl/c/CURLOPT_LOCALPORTRANGE.html)
[low-speed-limit](https://curl.haxx.se/libcurl/c/CURLOPT_LOW_SPEED_LIMIT.html)
[low-speed-time](https://curl.haxx.se/libcurl/c/CURLOPT_LOW_SPEED_TIME.html)
[maxconnects](https://curl.haxx.se/libcurl/c/CURLOPT_MAXCONNECTS.html)
[maxfilesize](https://curl.haxx.se/libcurl/c/CURLOPT_MAXFILESIZE_LARGE.html)
[maxredirs](https://curl.haxx.se/libcurl/c/CURLOPT_MAXREDIRS.html)
[max-send-speed](https://curl.haxx.se/libcurl/c/CURLOPT_MAX_SEND_SPEED_LARGE.html)
[max-recv-speed](https://curl.haxx.se/libcurl/c/CURLOPT_MAX_RECV_SPEED_LARGE.html)
[netrc](https://curl.haxx.se/libcurl/c/CURLOPT_NETRC.html)
[nobody](https://curl.haxx.se/libcurl/c/CURLOPT_NOBODY.html)
[noprogress](https://curl.haxx.se/libcurl/c/CURLOPT_NOPROGRESS.html)
[nosignal](https://curl.haxx.se/libcurl/c/CURLOPT_NOSIGNAL.html)
[password](https://curl.haxx.se/libcurl/c/CURLOPT_PASSWORD.html)
[path-as-is](https://curl.haxx.se/libcurl/c/CURLOPT_PATH_AS_IS.html)
[post](https://curl.haxx.se/libcurl/c/CURLOPT_POST.html)
[postfields](https://curl.haxx.se/libcurl/c/CURLOPT_COPYPOSTFIELDS.html)
[postfieldsize](https://curl.haxx.se/libcurl/c/CURLOPT_POSTFIELDSIZE_LARGE.html)
[protocols](https://curl.haxx.se/libcurl/c/CURLOPT_PROTOCOLS.html)
[proxy](https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html)
[proxyauth](https://curl.haxx.se/libcurl/c/CURLOPT_PROXYAUTH.html)
[proxyport](https://curl.haxx.se/libcurl/c/CURLOPT_PROXYPORT.html)
[proxytype](https://curl.haxx.se/libcurl/c/CURLOPT_PROXYTYPE.html)
[proxyuserpwd](https://curl.haxx.se/libcurl/c/CURLOPT_PROXYUSERPWD.html)
[proxy-ssl-verifypeer](https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html)
[proxy-ssl-verifyhost](https://curl.haxx.se/libcurl/c/CURLOPT_PROXY_SSL_VERIFYHOST.html)
[range](https://curl.haxx.se/libcurl/c/CURLOPT_RANGE.html)
[redir-protocols](https://curl.haxx.se/libcurl/c/CURLOPT_REDIR_PROTOCOLS.html)
[referer](https://curl.haxx.se/libcurl/c/CURLOPT_REFERER.html)
[request-target](https://curl.haxx.se/libcurl/c/CURLOPT_REQUEST_TARGET.html)
[resume-from](https://curl.haxx.se/libcurl/c/CURLOPT_RESUME_FROM_LARGE.html)
[ssl-verifyhost](https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html)
[ssl-verifypeer](https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html)
[tcp-keepalive](https://curl.haxx.se/libcurl/c/CURLOPT_TCP_KEEPALIVE.html)
[tcp-keepidle](https://curl.haxx.se/libcurl/c/CURLOPT_TCP_KEEPIDLE.html)
[tcp-keepintvl](https://curl.haxx.se/libcurl/c/CURLOPT_TCP_KEEPINTVL.html)
[timecondition](https://curl.haxx.se/libcurl/c/CURLOPT_TIMECONDITION.html)
[timeout](https://curl.haxx.se/libcurl/c/CURLOPT_TIMEOUT.html)
[timeout-ms](https://curl.haxx.se/libcurl/c/CURLOPT_TIMEOUT_MS.html)
[timevalue](https://curl.haxx.se/libcurl/c/CURLOPT_TIMEVALUE.html)
[unix-socket-path](https://curl.haxx.se/libcurl/c/CURLOPT_UNIX_SOCKET_PATH.html)
[unrestricted-auth](https://curl.haxx.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html)
[use-ssl](https://curl.haxx.se/libcurl/c/CURLOPT_USE_SSL.html)
[useragent](https://curl.haxx.se/libcurl/c/CURLOPT_USERAGENT.html)
[username](https://curl.haxx.se/libcurl/c/CURLOPT_USERNAME.html)
[userpwd](https://curl.haxx.se/libcurl/c/CURLOPT_USERPWD.html)
[verbose](https://curl.haxx.se/libcurl/c/CURLOPT_VERBOSE.html)
[wildcardmatch](https://curl.haxx.se/libcurl/c/CURLOPT_WILDCARDMATCH.html)

## Header Options

In addition to the normal libcurl special options that set headers
([useragent](https://curl.haxx.se/libcurl/c/CURLOPT_USERAGENT.html),
[referer](https://curl.haxx.se/libcurl/c/CURLOPT_REFERER.html),
[cookie](https://curl.haxx.se/libcurl/c/CURLOPT_COOKIE.html)), there
are some extra options for headers:

`Content-MD5`, `Content-Type`, `Content-Length`, `Host`, `Accept`,
`Expect`, `Transfer-Encoding`.

```
$curl.Host('somewhere.com');  # or $curl.setopt(Host => 'somewhere.com')
$curl.Content-MD5('...');     # or $curl.setopt(Content-MD5 => '...')

```

You can also add any other headers you like:

```
$curl.set-header(X-My-Header => 'something', X-something => 'foo');

```

You can clear a standard header by setting the header to '', or send a
header without content by setting the content to ';'

```
$curl.set-header(Accept => '');      # Don't send normal Accept header
$curl.set-header(Something => ';');  # Send empty header

```

If you are reusing the handle, you can also clear the set headers:

```
$curl.clear-header();

```

This only clears the 'extra' headers, not useragent/referer/cookie.

## Special Options

In addition to the normal libcurl options, Raku LibCurl uses options
for some special Raku functionality.

`debugfunction` replaces the libcurl
[CURLOPT\_DEBUGFUNCTION](https://curl.haxx.se/libcurl/c/CURLOPT_DEBUGFUNCTION.html)
callback, with one that looks like this:

```
sub debug(LibCurl::Easy $easy, CURL-INFO-TYPE $type, Buf $buf)
{...}

$curl.setopt(debugfunction => &debug);

```

`xferinfo` replaces the libcurl
[CURLOPT\_XFERINFOFUNCTION](https://curl.haxx.se/libcurl/c/CURLOPT_XFERINFOFUNCTION.html)
(and
[CURLOPT\_PROGRESSFUNCTION](https://curl.haxx.se/libcurl/c/CURLOPT_PROGRESSFUNCTION.html))
with one that looks like this:

```
sub xferinfo(LibCurl::Easy $easy, $dltotal, $dlnow, $ultotal, $ulnow)
{...}

$curl.setopt(xferinfofunction => &xferinfo);

```

`download` specifies a filename to download into.

`upload` specifies a filename to upload from.

`send` specifies a `Str` or a `Buf` to send content from.

Finally there is a `private` option which replaces
CURLOPT\_PRIVATE, and you can safely store any object in it.

## Errors

In most circumstances, errors from libcurl functions will result in a
thrown X::LibCurl exception. You can catch these with CATCH. You can
see the string error, or cast to Int to see the [libcurl error
code](https://curl.haxx.se/libcurl/c/libcurl-errors.html).

For HTTP transfers, you can access the response code with
`getinfo('response-code')` or just `.response-code`. You can also
check that the response code is in the 2xx range with `.success`.

You might find the
[failonerror](https://curl.haxx.se/libcurl/c/CURLOPT_FAILONERROR.html)
option useful to force an error if the HTTP code is equal to or larger
than 400. That will cause an exception in those cases.

On an error, you may find [extra human readable error
messages](https://curl.haxx.se/libcurl/c/CURLOPT_ERRORBUFFER.html)
with the `.error` method.

```
$curl.perform;

CATCH {
    say "Caught!";
    when X::LibCurl {
        say "$_.Int() : $_";
        say $curl.response-code;
        say $curl.error;
    }
}

```

## Info

After a transfer, you can retrieve internal information about the curl
session with the
[.getinfo](https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html)
method.

You can explicitly request a single field, or a list of fields to get
a hash, or just get all the fields as a hash. As in the options,
there are also convenience methods for each info field.

```
say $curl.getinfo('effective-url');
say $curl.getinfo('response-code');

say $curl.getinfo(<effective-url response-code>);  # Hash with those keys

say $curl.getinfo;   # Hash of all info fields

say $curl.effective-url;
say $curl.response-code;

```

Fields currently defined are:

[appconnect\_time](https://curl.haxx.se/libcurl/c/CURLINFO_APPCONNECT_TIME.html)
[certinfo](https://curl.haxx.se/libcurl/c/CURLINFO_CERTINFO.html)
[condition-unmet](https://curl.haxx.se/libcurl/c/CURLINFO_CONDITION_UNMET.html)
[connect-time](https://curl.haxx.se/libcurl/c/CURLINFO_CONNECT_TIME.html)
[content-type](https://curl.haxx.se/libcurl/c/CURLINFO_CONTENT_TYPE.html)
[cookielist](https://curl.haxx.se/libcurl/c/CURLINFO_COOKIELIST.html)
[effective-url](https://curl.haxx.se/libcurl/c/CURLINFO_EFFECTIVE_URL.html)
[ftp-entry-path](https://curl.haxx.se/libcurl/c/CURLINFO_FTP_ENTRY_PATH.html)
[header-size](https://curl.haxx.se/libcurl/c/CURLINFO_HEADER_SIZE.html)
[http-connectcode](https://curl.haxx.se/libcurl/c/CURLINFO_HTTP_CONNECTCODE.html)
[httpauth-avail](https://curl.haxx.se/libcurl/c/CURLINFO_HTTPAUTH_AVAIL.html)
[lastsocket](https://curl.haxx.se/libcurl/c/CURLINFO_LASTSOCKET.html)
[local-ip](https://curl.haxx.se/libcurl/c/CURLINFO_LOCAL_IP.html)
[local-port](https://curl.haxx.se/libcurl/c/CURLINFO_LOCAL_PORT.html)
[namelookup-time](https://curl.haxx.se/libcurl/c/CURLINFO_NAMELOOKUP_TIME.html)
[num-connects](https://curl.haxx.se/libcurl/c/CURLINFO_NUM_CONNECTS.html)
[os-errno](https://curl.haxx.se/libcurl/c/CURLINFO_OS_ERRNO.html)
[pretransfer-time](https://curl.haxx.se/libcurl/c/CURLINFO_PRETRANSFER_TIME.html)
[primary-ip](https://curl.haxx.se/libcurl/c/CURLINFO_PRIMARY_IP.html)
[primary-port](https://curl.haxx.se/libcurl/c/CURLINFO_PRIMARY_PORT.html)
[proxyauth-avail](https://curl.haxx.se/libcurl/c/CURLINFO_PROXYAUTH_AVAIL.html)
[redirect-url](https://curl.haxx.se/libcurl/c/CURLINFO_REDIRECT_URL.html)
[request-size](https://curl.haxx.se/libcurl/c/CURLINFO_REQUEST_SIZE.html)
[response-code](https://curl.haxx.se/libcurl/c/CURLINFO_RESPONSE_CODE.html)
[rtsp-client-cseq](https://curl.haxx.se/libcurl/c/CURLINFO_RTSP_CLIENT_CSEQ.html)
[rtsp-cseq-recv](https://curl.haxx.se/libcurl/c/CURLINFO_RTSP_CSEQ_RECV.html)
[rtsp-server-cseq](https://curl.haxx.se/libcurl/c/CURLINFO_RTSP_SERVER_CSEQ.html)
[rtsp-session-id](https://curl.haxx.se/libcurl/c/CURLINFO_RTSP_SESSION_ID.html)
[size-download](https://curl.haxx.se/libcurl/c/CURLINFO_SIZE_DOWNLOAD.html)
[size-upload](https://curl.haxx.se/libcurl/c/CURLINFO_SIZE_UPLOAD.html)
[speed-download](https://curl.haxx.se/libcurl/c/CURLINFO_SPEED_DOWNLOAD.html)
[speed-upload](https://curl.haxx.se/libcurl/c/CURLINFO_SPEED_UPLOAD.html)
[ssl-engines](https://curl.haxx.se/libcurl/c/CURLINFO_SSL_ENGINES.html)
[total-time](https://curl.haxx.se/libcurl/c/CURLINFO_TOTAL_TIME.html)

## Received header fields

You can retrieve the header fields in several ways as well.

```
say $curl.receiveheaders<Content-Length>;  # Hash of all headers

say $curl.get-header('Content-Length');

say $curl.Content-Length;

```

## Content

If you did not specify the `download` option to download content
into a file, the content will be stashed in memory in a Buf object you
can access with the `.buf()` method.

If you understand that the content is decodable as a string, you can
call the `.content($encoding = 'utf-8')` method which will decode
the content into a `Str`, by default with the **utf-8** encoding if
not specified.

```
say "Got content", $curl.content;

```

## Multi-part forms

Forms now uses the libcurl MIME capability, but requires verison 7.56
or greater for forms.

There is a special POST option for multipart/formdata.

```
my $curl = LibCurl::Easy.new(URL => 'http://...');

# normal field
$curl.formadd(name => 'fieldname', contents => 'something');

# Use a file as content, but not as a file upload
$curl.formadd(name => 'fieldname', filecontent => 'afile.txt');

# upload a file from disk, give optional filename or contenttype
$curl.formadd(name => 'fieldname', file => 'afile.txt',
              filename => 'alternate.name.txt',
              contenttype => 'image/jpeg');

# Send a Blob of contents, but as a file with a filename
$curl.formadd(name => 'fieldname', filename => 'some.file.name.txt',
              contents => "something".encode);

$curl.perform;

```

This will automatically cause LibCurl to POST the data.

## Proxies

[libcurl](https://curl.haxx.se/libcurl) has great proxy support, and
you should be able to specify anything needed as options to LibCurl to
use them. The easiest for most common cases is to just set the
[proxy](https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html) option.

By default, libcurl will also respect the environment variables
**http\_proxy**, **ftp\_proxy**, **all\_proxy**, etc. if any of those are
set. Setting the `proxy` string to `""` (an empty string) will
explicitly disable the use of a proxy, even if there is an environment
variable set for it.

A proxy host string can also include protocol scheme (`http://`) and
embedded user + password.

## Unix sockets

`LibCurl` can be used to communicate with a unix socket by setting the
`unix-socket-path` option. You must still specify a host, but it is
ignored. For example, you could use the [docker REST
API](https://docs.docker.com/engine/api/latest) like this:

```
use LibCurl::Easy;
use JSON::Fast;

my $docker = LibCurl::Easy.new(unix-socket-path => '/var/run/docker.sock');

my $info = from-json($docker.URL("http://localhost/info").perform.content);
say $info<KernelVersion>;
say $info<OperatingSystem>;

```

## Streaming

There is an experimental stream capability which can bind a Channel to
the data stream. Instead of retrieving the buffered data at once,
each time data comes in it is sent through to the Channel. This is
useful for long-lived connections that periodically send more data.

You can access it with Something like this:

```
my $curl = LibCurl::Easy.new(URL => ...);

my $stream = $curl.stream-out;

start react whenever $stream -> $in {
    say "in: ", $in;
}

$curl.perform;

```

Suggestions for improving the interface for this capability welcome!

## Multi

Raku LibCurl also supports the libcurl
[multi](https://curl.haxx.se/libcurl/c/libcurl-multi.html) interface.
You still have to construct `LibCurl::Easy` (or `LibCurl::HTTP`)
handles for each transfer, but instead of calling `.perform`, just add
the handle to a `LibCurl::Multi`.

```
use LibCurl::Easy;
use LibCurl::Multi;

my $curl1 = LibCurl::Easy.new(:verbose, :followlocation,
                     URL => 'http://example.com',
                     download => './myfile1.html');

my $curl2 = LibCurl::Easy.new(:verbose, :followlocation,
                     URL => 'http://example.com',
                     download => './myfile2.html');

my $multi = LibCurl:Multi.new;

$multi.add-handle($curl1);
$multi.add-handle($curl2);

$multi.perform;

say $curl1.statusline;
say $curl2.statusline;

```

You can also use an asynchronous callback to get a notification when
an individual transfer has completed. The callback takes place in the
same thread with all the transfers, so it should complete quickly (or
start a new thread for heavy lifting as needed). You can add
additional handles to the `LibCurl::Multi` at any time, even re-using
completed LibCurl::Easy handles (after setting `URL`, etc. as needed).

```
use LibCurl::Easy;
use LibCurl::Multi;

my $curl1 = LibCurl::Easy.new(:followlocation,
                              URL => 'http://example.com',
                              download => 'myfile1.html');

my $curl2 = LibCurl::Easy.new(:followlocation,
                              URL => 'http://example.com',
                              download => 'myfile2.html');

sub callback(LibCurl::Easy $easy, Exception $e)
{
    die $e if $e;
    say $easy.response-code;
    say $easy.statusline;
}

my $multi = LibCurl::Multi.new(callback => &callback);

$multi.add-handle($curl1, $curl2);

$multi.perform;

```

## INSTALL

`LibCurl` depends on [libcurl](https://curl.haxx.se/download.html), so
you must install that prior to installing this module. It may already
be installed on your system.

* For debian or ubuntu: `apt-get install libcurl4-openssl-dev`
* For alpine: `apk add libcurl`
* For CentOS: `yum install libcurl`

## SEE ALSO

There is another Raku interface to libcurl
[Net::Curl](https://github.com/azawawi/perl6-net-curl) developed by
Ahmad M. Zawawi. If you already use it and it works well for you,
great, keep using it. Ahmad did a nice job developing it. I would
encourage you to also take a look at this module. LibCurl provides a
more 'rakuish' OO interface to libcurl than Net::Curl, and wraps
things in a manner to make using it a little easier (IMHO).

## LICENSE

Copyright © 2017 United States Government as represented by the
Administrator of the National Aeronautics and Space Administration.
No copyright is claimed in the United States under Title 17,
U.S.Code. All Other Rights Reserved.

See [NASA Open Source Agreement](../master/NASA_Open_Source_Agreement_1.3%20GSC-17847.pdf) for more details.


## dist_zef-guifa-DateTime-Timezones.md
# DateTime::Timezones

> Phileas Fogg avait, «sans s'en douter», gagné un jour sur son itinéraire, - et cela uniquement parce qu'il avait fait le tour du monde en allant vers l'est, et il eût, au contraire, perdu un jour en allant en sens inverse, soit vers l'ouest.
> — *Le Tour du monde en quatre-vingts jours* (Jules Vernes)

An module to extend the built in `DateTime` with timezone support.
To use, simply include it at any point in your code:

```
use DateTime::Timezones;

my $dt = DateTime.new: now;

```

This extends `DateTime` to include the following three new attributes whose names *are subject to change*.

| attribute | type | information |
| --- | --- | --- |
| **`.olson-id`** | *Str* | The unique Olson ID for the timezone |
| **`.tz-abbr`** | *Str* | An (mostly) unique abbreviation for the timezone |
| **`.is-dst`** | *Bool* | Whether the timezone is in daylight savings time |

The Olson IDs (or timezone identifiers) are maintained by the IANA and based around city names, in the format of *Region/City*, and occasionally *Region/Subregion/City*.
They do not tend to align with popular usage.
In the United States, for instance, what is commonly called Eastern Time is listed as America/New\_York).

The timezone abbreviation is representative of popular usage, but isn't unique.
It's appropriate for a quick timestamp as it adjusts for daylight savings time, but for other display purposes, you may want to look to `Intl::Format::DateTime`.

The DST attribute returns whether the timezone is in what is commonly referred to as Daylight Saving Time (or Summer Time).
In some time zones, `False` is the only value.

### Additional Information

For the most part, once you enable it, you won't need to do anything different at all, as it is designed to be as discreet as possible.
There are, nonetheless, a few things to note:

* The default time zone is either **Etc/GMT**.
* The attribute `timezone` has been modified slightly to be allomorphic.
  For creation, you may pass either an `Int` offset *or* a `Str` Olson ID.
  Integer offsets are taken into account but the resultant time will be zoned to GMT (eventually whole-hour offsets will be be given an appropriate `Etc/` zone).
  When accessing `.timezone`, you get an `IntStr` comprising the offset and the Olson ID, so it should Just Work™.
  If you absolutely must have a strict `Int` value, use `.offset`, and for a strict `Str` value, use `.olson-id`
* The formatter is unchanged and only includes offset information, so round-tripping (`DateTime.new($datetime.Str)` may not preserve the Olson ID and DST information.
* Using `.later()`, `.earlier()` methods are currently untested.
  You may get unexpected results if you use them and cross a timezone transition.
* You should only use this on your executed script.
  Modules that wish to take advantage of `DateTime::Timezones` should *not* `use` it, and instead assume that the methods exist.
  If functionality is critical, you can try sticking in a `die unless DateTime.^can('is-dst')` that will be executed at runtime.
  This is due to a bug in Rakudo where the original methods of wrapped methods are deleted. I am working to create a workaround.

### Leapseconds

Leapseconds are annoying for timekeeping and POSIX explicitly ignores them since future ones are unpredictable because weird physics.
I do not have the expertise to ensure that leapseconds are handled correctly, but welcome any code review and/or pull requests to remedy this (particularly test cases).

## How does it work?

While the module initially planned on `augment`ing `DateTime`, it turns out that has significant problems for things like precompilation (you can't) and requires enabling `MONKEY-TYPING` which just feels dirty.

Instead, `DateTime.new` is wrapped with a new method that returns the same (or functionally the same) `DateTime` you would have expected and mixes in the parameterized `TimezoneAware` role.
It has a few tricks to make sure it doesn't apply the role multiple times.

## Version history

* **0.4.2**
  * Quickfix on dependency
* **0.4.1**
  * Fix bug calling `.timezone` (entered infinite loop)
  * Fix bug affecting year/month/day handling
* **0.4.0**
  * Timezone data reading moved to a new `Timezones::ZoneInfo` module
    * Links (in new module) are handled in code, reducing file size
    * Calculation errors fixed, especially for future dates, thanks to...
    * ...new handling of v.3 data files
    * Test files regarding accuracy moved to that module
  * Should "just work" when module is used inside of another module (though additional testing is still required here)
    * *Requires Rakudo 2021.10 or later*
* **0.3.9**
  * Fixed an issue caused by changes to the ZIC compiler
    * The `-b fat` option is used for backwards compatibility
    * Upcoming 0.4 release will better understand TZif v2+ files and fix some calculation errors
  * Updated to the 2021e release
    * Minor (but urgent) update for Palestine
* **0.3.8**
  * Updated to the 2021d release
    * Fiji updated (no DST for 2021–2022)
* **0.3.7**
  * Updated to the 2021c release
    * Fixed typos for the **Atlantic/Jan\_Mayen** link (and **America/Virgin**, we ignore *backzones*).
* **0.3.6**
  * Updated to the 2021b release
  * Jordan and Samoa updated
  * Zone mergers and renamings
  * Pre-1993 fixes for Malawi, Portugal, etc - **0.3.5**
  * Updated to the 2021a release
    * South Sudan, Russia (Volgograd), Turks and Caicos updated
    * Many other zones fixed for pre-1986 transitions
* **0.3.4**
  * Added support for historical Olson IDs (mainly to provide better support with CLDR)
  * Updated updater script to automatically back link older IDs.
* **0.3.3**
  * Upgraded database to the 2020d release
    * Minor (but urgent) update for Palestine
* **0.3.2**
  * Upgraded database to the 2020c release
    * Minor update for Fiji
  * Fixed bugs (found by ZeroDogg) caused when creating DateTimes with values other than `Int` or `Instant`
* **0.3.1**
  * Upgraded database to the 2020b release
    * Minor updates for Antarctica, Australia, Canada, and Morocco.
  * Fixed an install issue due to evil `.DS_Store` files (thanks to ZeroDogg for bringing it to my attention).
  * Minor adjustments to the updater script
* **0.3**
  * Support for 'upgrading' timezone-unaware `DateTime` objects that may have been precompiled before this module's `use` statement.
  * Additional test files.
  * Added an example: see `world-clock.raku` and pass in your favorite timezones
* **0.2.1**
  * Fixed creation from Instants by adding `.floor`
* **0.2**
  * TZif files generated on our own directly from the TZ database.
  * Fixed error in parsing leapseconds from TZif files
  * Fixed offset calculation error caused by misreading C code
  * Added test files (but more are needed, esp for leapseconds)
  * Created automated updater script for ease of maintenance.
* **0.1**
  * First release.
  * Basic support for creation of `DateTime` with timezone information.


## dist_zef-lizmat-IRC-Channel-Log.md
[![Actions Status](https://github.com/lizmat/IRC-Channel-Log/workflows/test/badge.svg)](https://github.com/lizmat/IRC-Channel-Log/actions)

# NAME

IRC::Channel::Log - access to all logs of an IRC channel

# SYNOPSIS

```
use IRC::Log::Colabti;  # class implementing one day of logs
use IRC::Channel::Log;

my $channel = IRC::Channel::Log.new(
  logdir    => "logs/raku",                   # directory containing logs
  class     => IRC::Log::Colabti,             # for example
  generator => -> $nick { RandomColor.new },  # generate color for nick
);

say $channel.dates;             # the dates for which there are logs available
say $channel.problems.elems;    # hash with problems / date

.say for $channel.entries;      # all entries of this channel

.say for $channel.entries(
  :conversation,              # only return conversational messages
  :control,                   # only return control messages
  :dates<2021-04-23>,         # limit to given date(s)
  :nick-names<lizmat japhb>,  # limit to given nick(s)
  :contains<foo>,             # limit to containing given text
  :starts-with<m:>,           # limit to starting with given text
  :matches(/ /d+ /),          # limit to matching regex
);

$channel.watch-and-update;  # watch and process updates

$channel.shutdown;          # perform all necessary actions on shutdown

```

# DESCRIPTION

IRC::Channel::Log provides a programmatic interface to the IRC log files of a channel.

# CLASS METHODS

## new

```
use IRC::Log::Colabti;  # class implementing one day of logs
use IRC::Channel::Log;

my $channel = IRC::Channel::Log.new(
  logdir    => "logs/raku",        # directory containing logs
  class     => IRC::Log::Colabti,  # class implementing log parsing logic
  generator => &generator,        # generate color for nick
  name      => "foobar",           # name of channel, default: logdir.basename
  state     => "state",            # directory containing persistent state info
  batch     => 1,                  # number of logs parsed / time, default: 16
  degree    => 8,                  # threads used, default: Kernel.cpu-cores/2
);

```

The `new` class method returns an `IRC::Channel::Log` object. It takes four named arguments:

### :logdir

The directory (either as a string or as an `IO::Path` object) in which log file of the channel is located, as created by a logger such as `IRC::Client::Plugin::Logger`. This expects the directories to be organized by year, with all the logs of each day of a year in that directory. For example, in the test of this module:

```
raku
 |-- 2021
      |-- 2021-04-23
      |-- 2021-04-24

```

This argument is required.

### :batch

The batch size to use when racing to read all of the log files of the given channel. Defaults to 16 as an apparent optimal values to optimize for wallclock and not have excessive CPU usage. You can use `:!batch` to indicate you do not want any multi-threading: this is equivalent to specifying `1` or `0` or `True`.

### :class

The class to be used to interpret log files, e.g. `IRC::Log::Colabti`. This argument is also required.

### :degree

The maximum number of threads to be used when racing to read all of the log files of the given channel. Defaults to `Kernel.cpu-cores/2` (aka half the number of CPU cores the system claims to have).

### :generator

A `Callable` expected to take a nick and return a color to be associated with that nick.

### :name

The name of the channel. Optional. Defaults to the base name of the directory specified with `logdir`.

### :state

The directory (either as a string or as an `IO::Path` object) in which persistent state information of the channel is located.

# INSTANCE METHODS

## active

```
say "$channel.name() is active" if $channel.active;

```

The `active` instance method returns whether the channel is considered to be active. If a `state` directory has been specified, and that directory contains a file named "inactive", then the channel is considered to **not** be active.

## aliases-for-nick-name

```
my @aliases = $channel.aliases-for-nick-name($nick);

```

The `aliases-for-nick-name` instance method returns a sorted list of nick names that are assumed to be aliases (aka, have the same color) for the given nick name.

## cleaned-nick

```
my $cleaned = $channel.cleaned-nick-name($nick);

```

The `cleaned-nick-name` instance method returns the cleaned version of the given nick nae, which is used to group together the aliases of a nick name.

## dates

```
say $channel.dates;          # the dates for which there are logs available

```

The `dates` instance method returns a sorted list of dates (as strings in YYYY-MM-DD format) of which there are entries available.

## entries

```
.say for $channel.entries;             # all entries in chronological order

.say for $channel.entries(:reverse);   # in reverse chronological order

.say for $channel.entries(:contains<question>); # containing text

.say for $channel.entries(:control);            # control messages only

.say for $channel.entries(:conversation);       # conversational messages only

.say for $channel.entries(:dates<2021-04-23>);  # for one or more dates

.say for $channel.entries(:matches(/ \d+ /);    # matching regex

.say for $channel.entries(:starts-with<m:>);    # starting with text

.say for $channel.entries(:contains<foo>);      # containing string

.say for $channel.entries(:words<foo>);         # containing word

.say for $channel.entries(:nicks<lizmat>);      # for one or more nicks

.say for $channel.entries(:before-target($target);  # entries before target

.say for $channel.entries(:from-target($target);    # entries from target

.say for $channel.entries(:after-target($target);   # entries after target

.say for $channel.entries(:around-target($target);  # entries around target

.say for $channel.entries(:@targets);           # entries of given targets

.say for $channel.entries(
  :dates<2021-04-23>,
  :nicks<lizmat japhb>,
  :contains<question answer>, :all,
);

```

The `entries` instance method is *the* workhorse for selecting entries from the log. It will always return messages in chronological order.

It takes a number of (optional) named arguments that allows you to select the entries from the log that you need. Named arguments may be combined, although some combinations do not make a lot of sense (which will generally mean that some of them will be ignored).

If no (valid) named arguments are specified, then **all** entries from the log will be produced: this allows you to do any ad-hoc filtering.

The following named arguments are supported:

### :after-target

```
$channel.entries(:after-target<2021-04-23Z23:36>);

```

The `after-target` named argument can be used with any of the other named arguments (with the exception of `reverse`, which it overrides as `:!reverse`). It will limit any entries to those that have a `target` value **greater** than the value provided.

Targets are formatted as `YYYY-MM-DDZHH:MM-NNNN` with the `-NNNN` removed if it would have been `-0000`.

### :all

The `all` named argument can only be used in combination with the `contains` and `words` named arguments. If specified with a true value, it will force entries to only be selected if **all** conditions are true.

### :around-target

```
$channel.entries(:around-target<2021-04-23Z23:36>);  # default 10 entries

$channel.entries(:around-target<2021-04-23Z23:36>, :nr-entries(5));

```

The `around-target` named argument can be used with any of the other named arguments (with the exception of `reverse`). It will return any entries before and after to the entry with the `target`. By default, **10** entries will be selected before **and** after the target (thus returning a maximum of 21 entries). The `nr-entries` named argument can be used to indicate the number of entries before / after should be fetched.

Targets are formatted as `YYYY-MM-DDZHH:MM-NNNN` with the `-NNNN` removed if it would have been `-0000`.

### :before-target

```
$channel.entries(:after-target<2021-04-24Z02:50>);

```

The `before-target` named argument can be used with any of the other named arguments (with the exception of `reverse`, which it overrides as `:reverse`). It will limit any entries to those that have a `target` value **smaller** than the value provided.

Targets are formatted as `YYYY-MM-DDZHH:MM-NNNN` with the `-NNNN` removed if it would have been `-0000`.

### :contains

```
# just "question"
$channel.entries(:contains<question>);

# "question" or "answer"
$channel.entries(:contains<question answer>);

# "question" or "answer"
$channel.entries(:contains<question answer>, :ignorecase);

# "question" and "answer"
$channel.entries(:contains<question answer>, :all);

```

The `contains` named argument allows specification of one or more strings that an entry should contain. By default, an entry should only contain one of the specified strings to be selected. If you want an entry to contain **all** strings, then an additional `:all` named argument can be specified (with a true value).

If comparisons need to be done in an case-insensitive manner, then the `ignorecase` named argument can be specified with a true value.

Since this only applies to conversational entries, any additional setting of the `conversation` or `control` named arguments are ignored.

### :control

```
$channel.entries(:control);   # control messages only

$channel.entries(:!control);  # all but control messages

```

The `control` named argument specifies whether to limit to control messages only or not.

### :conversation

```
$channel.entries(:conversation);   # conversational messages only

$channel.entries(:!conversation);  # all but conversational messages

```

The `conversation` named argument specifies whether to limit to conversational messages only or not.

### :dates

```
$channel.entries(:dates<2021-04-23>);   # just on one date

my @dates = Date.today ... Date.today.earlier(:3days);
$channel.entries(:@dates);              # multiple dates

```

The `dates` named argument allows one to specify the date(s) from which entries should be selected. Dates can be specified in anything that will stringify in the YYYY-MM-DD format, but are expected to be in ascending sort order.

### :from-target

```
$channel.entries(:from-target<2021-04-23Z23:36>);

```

The `from-target` named argument can be used with any of the other named arguments (with the exception of `reverse`, which it overrides as `:!reverse`). It will limit any entries to those that have a `target` value **greater than or equal to** the value provided.

Targets are formatted as `YYYY-MM-DDZHH:MM-NNNN` with the `-NNNN` removed if it would have been `-0000`.

### :ignorecase

The `ignorecase` named argument can only be used in combination with the `starts-with`, `contains` and `words` named arguments. If specified with a true value, it will do all comparisons in a case-insensitive manner.

### :matches

```
$channel.entries(:matches(/ \d+ /);    # matching regex

```

The `matches` named argument allows one to specify a `Regex` to indicate which entries should be selected.

Since this only applies to conversational entries, any additional setting of the `conversation` or `control` named arguments are ignored.

### :nick-names

```
$channel.entries(:nick-names<lizmat>);        # limit to "lizmat"

$channel.entries(:nick-names<lizmat japhb>);  # limit to "lizmat" or "japhb"

```

The `nick-names` named argument allows one to specify one or more nick names to indicate which entries should be selected.

### :nr-entries

The `nr-entries` named argument can only be used in combination with the `around-target` argument. It specifies how many entries should be fetched before / after the given target.

### :reverse

```
.say for $channel.entries(:reverse);   # all messages, most recent first
.say for $channel.entries(:!reverse);  # all messages, oldest first

```

The `reverse` named argument allows one to specify the order in which entries will be returned. If specified with a true value, it will return the most recent entries first, in reverse chronological order.

### :starts-with

```
# starts with "m:"
$channel.entries(:starts-with<m:>);

# starts with "how" in any case
$channel.entries(:starts-with<how>, :ignorecase);

# starts with "m:" or "j:"
$channel.entries(:starts-with<m: j:>);

```

The `start-with` named argument allows specification of one or more strings that an entry should start with.

If comparisons need to be done in an case-insensitive manner, then the `ignorecase` named argument can be specified with a true value.

Since this only applies to conversational entries, any additional setting of the `conversation` or `control` named arguments are ignored.

### :targets

```
.say for $channel.entries(:@targets);

```

The `targets` named argument allows specification of one or more targets for which to return the associated entry.

### :words

```
# contains the word "foo"
$channel.entries(:words<foo>);

# contains the word "foo" or "bar"
$channel.entries(:words<foo bar>);

# contains the word "foo" or the word "bar" in any case
$channel.entries(:words<foo bar>, :ignorecase);

# contains the word "foo" *and* "bar"
$channel.entries(:words<foo bar>, :all);

```

The `words` named argument allows specification of one or more words that an entry should contain. By default, an entry should only contain one of the specified words to be selected. If you want an entry to contain **all** words, then an additional `:all` named argument can be specified (with a true value).

If comparisons need to be done in an case-insensitive manner, then the `ignorecase` named argument can be specified with a true value.

Since this only applies to conversational entries, any additional setting of the `conversation` or `control` named arguments are ignored.

## initial-topic

```
say $channel.initial-topic($date);

```

The `initial-topic` instance method takes a date (either as a `Date` object or as a string) and returns the log entry of the topic (as a ::Topic object) of the channel on that date. Returns `Nil` if no topic is known for the given date.

## is-first-date-of-month

```
say $channel.is-first-date-of-month($date);

```

The `is-first-date-of-month` instance method takes a date (either as a `Date` object or as a string) and returns whether that date is the first date of the month, according to availability in the logs.

## is-first-date-of-year

```
say $channel.is-first-date-of-year($date);

```

The `is-first-date-of-month` instance method takes a date (either as a `Date` object or as astring) and returns whether that date is the first date of the year, according to availability in the logs.

## log

```
say $channel.log($date);  # log object for given date

```

The `log` instance method takes a string representing a date, and returns the `class` object for that given date. Returns `Nil` if there is no log available on the specified date.

## next-date

```
say $channel.next-date($date);  # date after the given date with a log

```

The `next-date` instance method takes a string representing a date, and returns a string with the **next** date of logs that are available. Returns `Nil` if the specified date is the last date or after that.

## nick-names

```
say $channel.nick-names;  # the nick names for which there are logs available

```

The `nick-names` instance method returns a sorted list of all the nick names seen.

## prev-date

```
say $channel.prev-date($date);  # date before the given date with a log

```

The `prev-date` instance method takes a string representing a date, and returns a string with the **previous** date of logs that are available. Returns `Nil` if the specified date is the first date or before that.

## problems

```
.say for $channel.problems;  # the dates with log parsing problems

```

The `problems` instance method returns a sorted list of `Pair`s with the date (formatted as YYYY-MM-DD) as key, and a list of problem descriptions as value.

## sc

```
say "$channel.sc.elems() nick to color mappings are known";

```

The `String::Color` object that contains the mapping of nick to color.

## shutdown

```
$channel.shutdown;

```

Performs all the actions needed to shutdown: specifically saves the nick to color mapping if a `state` directory was specified.

## state

```
say "state is saved in $channel.state()";

```

The `IO` object of the directory in which persistent state will be saved.

## this-date

```
say $channel.this-date($date);  # date after / before the given date

```

The `this-date` instance method takes a string representing a date, and returns a string with the **first** date of logs that are available. This could be either the given date, or the next date, or the previous date (if there was no next date). Returns `Nil` if no dates could be found, which would effectively mean that there are **no** dates in the log.

## watch-and-update

```
$channel.watch-and-update;

$channel.watch-and-update post-process => {
    say "$channel.name(): $_.message()"
}

```

The `watch-and-update` instance method starts a thread (and returns its `Promise` in which it watches for any updates in the most recent logs. If there are any updates, it will process them and make sure that all the internal state is correctly updated.

It optionally takes a `post-process` named argument with a `Callable` that will be called with any `IRC::Log` entries that have been added to the channel log.

# AUTHOR

Elizabeth Mattijsen [liz@raku.rocks](mailto:liz@raku.rocks)

# COPYRIGHT AND LICENSE

Copyright 2021 Elizabeth Mattijsen

Source can be located at: <https://github.com/lizmat/IRC-Channel-Log> . Comments and Pull Requests are welcome.

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.