# Variant Sources Plugin

## NAME

`VariantsSourcesPlugin` и extension `variantSources`.

## SYNOPSIS

```groovy
plugins {
    id 'org.implab.gradle-variants-sources'
}

variants {
    layer('main')

    variant('browser') {
        role('main') { layers('main') }
    }

    variant('node') {
        role('main') { layers('main') }
    }
}

variantSources {
    bind('main').sourceSetNamePattern = '{layer}'

    bind('main') {
        configureSourceSet {
            declareOutputs('compiled')
        }
    }

    whenRegistered { sourceSetName() }
    whenBound('browser') { roleName() }
}
```

## DESCRIPTION

`VariantsSourcesPlugin` применяет `VariantsPlugin` и `SourcesPlugin`, затем
регистрирует source sets из модели `variants`.

Точка запуска registration:

- `variants.whenFinalized(model -> registerSourceSets(...))`

### registration

Для каждой usage-связки `variant/role/layer` вычисляется имя source set,
регистрируется `GenericSourceSet` (если он еще не существует), затем
вызываются callbacks.

### binding

`bind('<layer>')` возвращает `LayerBindingSpec` и задает policy для этого
слоя:

- как именовать source set;
- как конфигурировать source set;
- какие callbacks вызвать на registration/binding.

### sourceSetNamePattern

`sourceSetNamePattern` определяет naming policy зарегистрированного source set.

Default:

- `{variant}{layerCap}`

Tokens:

- `{variant}`, `{variantCap}`
- `{role}`, `{roleCap}`
- `{layer}`, `{layerCap}`

Имя санитизируется (`[^A-Za-z0-9_.-] -> _`).

Ограничение:

- один `sourceSetName` не может быть порожден разными слоями.

## EVENTS

### whenRegistered

- callback на новый уникальный source set;
- replayable;
- при shared source set срабатывает один раз.

### whenBound

- callback на каждую usage-связку `variant/role/layer`;
- replayable;
- подходит для per-usage логики.

### variant filter

Фильтр по варианту поддерживает только usage-binding:

- `whenBound(String variantName, ...)`

## PAYLOAD TYPES

`SourceSetRegistration` содержит:

- `layerName`, `sourceSetName`;
- `sourceSet` (`NamedDomainObjectProvider<GenericSourceSet>`).

Sugar:

- `configureSourceSet(Action|Closure)`.

`SourceSetUsageBinding` содержит:

- `variantName`, `roleName`, `layerName`, `sourceSetName`;
- `sourceSet` (`NamedDomainObjectProvider<GenericSourceSet>`).

Sugar:

- `configureSourceSet(Action|Closure)`.

## API

### VariantSourcesExtension

- `bind(BuildLayer)` — получить/создать binding для canonical layer identity.
- `bind(String)` — получить/создать binding по имени слоя.
- `bind(String, Action|Closure)` — сконфигурировать binding.
- `bind(BuildLayer, Action|Closure)` — сконфигурировать binding по `BuildLayer`.
- `getBindings()` — read-only snapshot текущих bindings.
- `whenRegistered(...)` — глобальные callbacks регистрации source set.
- `whenBound(...)` — глобальные callbacks usage-binding.
- `whenBound(String variantName, ...)` — usage-binding callbacks с variant-filter.

### LayerBindingSpec

- `sourceSetNamePattern` — naming policy для source set слоя.
- `configureSourceSet(...)` — слойная конфигурация `GenericSourceSet`.
- `whenRegistered(...)` — callbacks регистрации в рамках слоя.
- `whenBound(...)` — callbacks usage-binding в рамках слоя.

## KEY CLASSES

- `VariantsSourcesPlugin` — точка входа plugin adapter.
- `VariantSourcesExtension` — глобальный DSL bind/events.
- `LayerBindingSpec` — публичный DSL-contract layer-local policy/callbacks.
- `SourceSetRegistration` — payload регистрации source set.
- `SourceSetUsageBinding` — payload usage-binding.

## NOTES

- `sourceSetNamePattern` фиксируется при первом чтении в registration
  (`finalizeValueOnRead`).
- runtime state bindings скрыт внутри adapter implementation (`LayerBinding`).
- name-based bindings резолвятся к canonical `BuildLayer` через registry `variants`.
- Closure callbacks используют delegate-first.
- Для вложенных closure лучше явный параметр (`ctx -> ...`).
