blob: 40c37266f42700a3ede2f26eeda7c71577d719f7 [file] [edit]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file
part of '../control.dart';
/// A [Conditional] branch.
///
/// Intentionally not exported to avoid confusion
/// with [Conditional].
///
@internal
abstract class Branch implements Built<Branch, BranchBuilder> {
Branch._();
factory Branch([void Function(BranchBuilder) updates]) = _$Branch;
Expression? get condition;
Block get body;
_Branch _buildable(bool isElse) =>
_Branch(condition: condition, body: body, isElse: isElse);
}
/// Builds a [Conditional] branch.
abstract class BranchBuilder implements Builder<Branch, BranchBuilder> {
BranchBuilder._();
factory BranchBuilder() = _$BranchBuilder;
/// The `if` statement condition.
///
/// If this is the first branch in a [Conditional], [condition] is
/// required. Otherwise, it may be left `null` to create an `else` statement.
Expression? condition;
/// The branch body.
BlockBuilder body = BlockBuilder();
/// Set [condition] to an `if-case` expression, matching [object] against
/// [pattern].
///
/// Guard clause [guard] can also be specified.
///
/// ```dart
/// if (object case pattern)
/// if (object case pattern when guard)
/// ```
///
/// Equivalent to using [ControlFlow.ifCase].
void ifCase({
required Expression object,
required Expression pattern,
Expression? guard,
}) =>
condition = ControlFlow.ifCase(
object: object,
pattern: pattern,
guard: guard,
);
}
/// Buildable version of [Branch]
class _Branch extends _$Branch with ControlBlock {
final bool isElse;
_Branch({required super.condition, required super.body, required this.isElse})
: super._();
ControlExpression? get _condition =>
condition == null ? null : ControlExpression.ifStatement(condition!);
@override
ControlExpression get _expression =>
isElse
? ControlExpression.elseStatement(_condition)
: (_condition ?? throwError);
Never get throwError {
throw ArgumentError(
'The first branch in a conditional must specify a condition',
'condition',
);
}
}
/// Represents a conditional (`if`/`else`) tree.
///
/// The first added branch will be treated as an `if` block, with
/// all subsequent conditions being treated as `else`.
///
/// {@category controlFlow}
abstract class Conditional
with ControlTree
implements Built<Conditional, ConditionalBuilder> {
Conditional._();
/// Build an [Conditional]
factory Conditional(void Function(ConditionalBuilder tree) builder) =
_$Conditional;
@protected
@visibleForTesting
BuiltList<BranchBuilder> get branches;
@override
List<ControlBlock> get _blocks =>
branches
.mapIndexed(
(index, element) => element.build()._buildable(index != 0),
)
.toList();
/// Builds a branch with [builder] and returns
/// a new [Conditional] with it added to the tree.
Conditional elseIf(void Function(BranchBuilder branch) builder) =>
(toBuilder()..branches.add(BranchBuilder()..update(builder))).build();
/// Builds a block with [builder] and returns a new [Conditional]
/// with it added to the tree as an `else` [Branch].
Conditional orElse(void Function(BlockBuilder body) builder) =>
elseIf((block) => builder(block.body));
}
/// Builds a [Conditional].
///
/// The first added branch will be treated as an `if` block, with
/// all subsequent conditions being treated as `else`.
///
/// {@category controlFlow}
abstract class ConditionalBuilder
implements Builder<Conditional, ConditionalBuilder> {
ConditionalBuilder._();
factory ConditionalBuilder() = _$ConditionalBuilder;
/// The items in this tree.
ListBuilder<BranchBuilder> branches = ListBuilder();
/// Build a branch with [builder] and add it to the conditional tree.
///
/// The first branch will be an `if` block, and all subsequent branches
/// will be `else if` or `else`.
void add(void Function(BranchBuilder branch) builder) =>
branches.add(BranchBuilder()..update(builder));
/// Shorthand to build a block with no condition and add it to the tree.
void addElse(void Function(BlockBuilder body) builder) =>
branches.add(BranchBuilder()..body.update(builder));
}