[![Actions Status](https://github.com/raku-community-modules/CompUnit-Util/actions/workflows/linux.yml/badge.svg)](https://github.com/raku-community-modules/CompUnit-Util/actions) [![Actions Status](https://github.com/raku-community-modules/CompUnit-Util/actions/workflows/macos.yml/badge.svg)](https://github.com/raku-community-modules/CompUnit-Util/actions) [![Actions Status](https://github.com/raku-community-modules/CompUnit-Util/actions/workflows/windows.yml/badge.svg)](https://github.com/raku-community-modules/CompUnit-Util/actions)

NAME
====

CompUnit::Util - Utilities for introspecting compunits and re-exporting

SYNOPSIS
========

```raku
use CompUnit::Util;
```

DESCRIPTION
===========

CompUnit::Util contains set of utilities to introspect `CompUnit` stuff and a bunch of compile time symbol manipulation tools. Its main purpose is to encapsulate the compiler hacks needed to implement features like 're-exporting' which don't exist in rakudo yet.

**WARNING** this module relies on unspec'd rakudo internals and can (and has and will) break without warning.

CompUnit UTILITIES
==================

`CompUnit` introspection utilities.

Apart from `load` none of the routines here will load a compunit. All parameters named `$handle` are converted to a `CompUnit::Handle` object automatically. If the `$handle` you pass is not a defined `CompUnit` or `CompUnit::Handle`, `&find-loaded` will be used toi search for a loaded compunit matching it.

load
----

```raku
use CompUnit::Util :load;
my CompUnit $cu = load('Test');
# or even
my $cu = load('MyModule', version => v3);
```

Loads a compunit by name. All named arguments to `CompUnit::DependencySpecification` are accepted (other than `short-name` which is the positional argument). At the moment `load` is just short for:

```raku
$*REPO.need(CompUnit::Dependencyspecification.new(:short_name<MyModule>));
```

find-loaded
-----------

```raku
use CompUnit::Util :find-loaded;
need SomeModule;
my CompUnit $some-module = find-loaded('SomeModule');
```

Searches all the `CompUnit::Repository`s until it finds a loaded compunit matching `$match`. Returns a failure otherwise.

all-loaded
----------

```raku
use CompUnit::Util :all-loaded;
.note for all-loaded;
```

Returns all loaded `CompUnit`s.

at-unit
-------

```raku
use CompUnit::Util :at-unit;
say at-unit('CompUnit::Util','$=pod');
```

Gets a symbol from the `UNIT` scope of the compunit. If you want to do this at compile time while a compunit is loading see `get-unit`.

unit-to-hash
------------

```raku
use CompUnit::Util :unit-to-hash;
my %unit := unit-to-hash('SomeModule');
```

Returns a `Hash` representing the `UNIT::` of the module.

capture-import
--------------

```raku
use CompUnit::Util :capture-import;
need SomeModule;
my %symbols = capture-import('SomeModule',:tag);
```

Attempts to simulate a `use` statement. Returns a hash of all the symbols the compunit would export if it were `use`d.

WHO Utilities
=============

set-in-WHO
----------

```raku
use CompUnit::Util :who;
my package Example {};
BEGIN set-in-WHO(Example.WHO,'Foo::Bar::$Baz','win');

say Example::Foo::Bar::<$Baz>; #-> win
```

Convenience routine for setting a symbol's value inside a package that might not exist yet. Only useful outside the compunit being compiled.

descend-WHO
-----------

```raku
use CompUnit::Util :who;
my package Example {};
BEGIN set-in-WHO(Example.WHO,'Foo::Bar::Baz','win');
BEGIN note descend-WHO(Example.WHO,'Foo::Bar::Baz'); #-> win
```

Convenience routine for getting a symbol's value with a path from a `Stash` like `.WHO`. Only useful outside the currently compiling compunit (where you can just use the normal syntax).

## Re-Exporting

The following routines provide re-exporting which is not yet implemented in rakudo.

re-export
---------

**This routine can only be called at `BEGIN` time**

```raku
use CompUnit::Util :re-export;
need SomeModule;
BEGIN re-export('SomeModule');
# This compunit will now export everything that SomeModule does
```

Merges the `EXPORT` package from `$handle` into the present `UNIT::EXPORT`.

re-exporthow
------------

**This routine can only be called at `BEGIN` time**

```raku
use CompUnit::Util :re-export;
need SomeModule;
BEGIN re-exporthow('SomeModule');
# This compunit now exports SomeModule's custom declarators
```

Merges the `EXPORTHOW` from `$handle` into the present `UNIT::EXPORTHOW`. `UNIT::EXPORTHOW` will be created if it doesn't exist but it won't clobber it if it does.

steal-export-sub
----------------

**This routine can only be called at `BEGIN` time**

```raku
use CompUnit::Util :re-export;
need SomeModule;
BEGIN steal-export-sub('SomeModule');
# This compunit now has the same &EXPORT as SomeModule
```

Sets `UNIT::<&EXPORT>` to `$handle`'s `&EXPORT`.

steal-globalish
---------------

**This routine can only be called at `BEGIN` time**

```raku
use CompUnit::Util :re-export,:load;
BEGIN steal-globalish(load('SomeModule'));
# This compunit now has everything in SomeModule in it's globalish
```

Merges the `GLOBALish` from `$handle` into the present `UNIT::GLOBALish`.

This is the least interesting of all the re-exports, and if you've already done `need SomeModule;` you won't need it. But it's here for completeness. The above example should be the same as this anyway:

```raku
BEGIN require ::('SomeModule');
```

re-export-everything
--------------------

**This routine can only be called at `BEGIN` time**

```raku
use CompUnit::Util :re-export;
BEGIN re-export-everything('SomeModule');
# use [this-module]; should now do the same thing as use SomeModule;
```

A convenience method for calling all the other functions under re-export functions with the same argument.

SYMBOL SETTING
==============

The following routines manipulate the symbols of the compunit being compiled. They are probably of most use inside an `&EXPORT` sub or in a trait.

They each take a map of symbol names to values and install them in different places.

**NOTE:** These subs will overwrite existing symbols without warning.

Inserts name/value pairs into the present `UNIT::EXPORT` under `$tag`.

set-unit
--------

**This routine can only be called at `BEGIN` time**

```raku
# like is export, but prefixes the the exported name with 'fun-'
sub trait_mod:<is>(Routine:D $r, :$export-fun!) {
    my $exported-name = '&fun-' ~ $r.name;
    set-unit("EXPORT::DEFAULT::{$exported-name}",$r);
    {};}
```

Inserts the `$value` at `$path` in `UNIT` of the currently compiling compunit.

set-lexpad
----------

**This routine can only be called at `BEGIN` time**

The same as `set-unit` but inserts the $value into the lexical scope being compiled.

SYMBOL GETTING
==============

get-unit
--------

```raku
use CompUnit::Util :get-symbols;
sub foo is export { };
BEGIN note get-unit('EXPORT::DEFAULT::&foo') === &foo; #-> True
```

get-lexpad
----------

The same as `get-unit` but looks for the symbol in the lexpad being compiled.

get-lexical
-----------

Like `get-lexpad` but does a full lexical lookup. At the moment it can only take a single `$name` with no `::`.

DISPATCHER MANIPULATION
=======================

These routines help you construct `multi` dispatchers **candidate by candidate** in a procedural manner. Useful when you want to construct a trait that adds a multi candidate each time it's called. Parameters marked `$multi` can be any `Routine:D`. If you pass a dispatcher it will just use it as the dispatcher or die if you are trying to push onto an existing dispatcher.

If you try and push a non-multi/dispatcher onto an empty slot it will not vivify one for you.

push-multi
----------

Adds the `$candidate` onto `$target`.

```raku
use CompUnit::Util :push-multi;
multi foo('one') { 'one' }
multi foo('two') { 'two' }

&foo.&push-multi(sub ('three') { "win" });
say foo('three') #-> "win"
```

**NOTE:** This is NYI in rakudo. The design docs says that protos should have a `.push` method. See [S06](https://github.com/Raku/old-design-docs/blob/master/S06-routines.pod).

push-unit-multi
---------------

**This routine can only be called at `BEGIN` time**

```raku
## lib/SillyModule.rakumod
use CompUnit::Util :push-multi
# exports the multi under a sub named after its first letter
sub trait_mod:<is>(Routine:D $r,:$one-letter-export!) {
    my $exported-name = '&' ~ $r.name.comb[0];
    push-unit-multi("EXPORT::DEFAULT::{$exported-name}",$r);
}

multi bar(Str) is one-letter-export { say "bar" }
multi baz(Int) is one-letter-export { say "baz" }

...
use SillyModule;

b("string"); #-> bar
b(1) #-> baz
```

Takes `$multi` and pushses it onto a dispatcher located at `$path`. If one doesn't exist it will be created. You can pass a `proto` instead of a multi but only when `$path` is empty (ie only the first time). It will become the dispatcher for any further calls.

push-lexpad-multi
-----------------

**This routine can only be called at `BEGIN` time**

The same as `push-unit-multi` but pushes onto a symbol in the lexical scope currently being compiled.

push-lexical-multi
------------------

**This routine can only be called at `BEGIN` time**

The smart version of `push-lexpad-multi`. If it doesn't find a dispatcher in the current lexpad it will do a lexical lookup for one of the same `$name`. If it finds one it clones it, installs it in the current lexpad and pushes `$multi` onto it. Like `get-lexical`, it can't take a `$name` with `::` in it.

AUTHOR
======

Lloyd Fournier

Source can be located at: https://github.com/raku-community-modules/CompUnit-Util . Comments and Pull Requests are welcome.

COPYRIGHT AND LICENSE
=====================

Copyright 2016 - 2023 Lloyd Fournier

Copyright 2026 Raku Community

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

