getChild method

WoFormNode<Object?>? getChild({
  1. required String path,
  2. required String parentPath,
  3. required WoFormValues values,
})

The path of an input is the ids of it and its parents, separated by the character '/'.

Exemple :

InputsNode( id: 'profile', inputs: [ StringInput( id: 'name', ...

The path of the input with id 'name' is '/profile/name'.

Implementation

WoFormNode? getChild({
  /// The path of the child, relative to this node (contains this node's id)
  required String path,

  /// The path until this node
  required String parentPath,
  required WoFormValues values,
}) {
  if (!path.startsWith('/')) {
    throw ArgumentError('An input path must start with character "/".');
  }

  final secondSlashIndex = path.substring(1).indexOf('/');

  switch (this) {
    case ConditionnalNode(:final condition, :final child):
      if (!values.meet(condition)) return null;

      // if the path ends at the child of this node
      if (secondSlashIndex == -1) {
        return (child.id == path.substring(1)) ? child : null;
      }

      return child.getChild(
        path: path.substring(secondSlashIndex + 1),
        parentPath: '$parentPath/$id',
        values: values,
      );
    case FutureNode(:final builder):
      final snapshot = values.get<AsyncSnapshot<T?>>('$parentPath/$id');
      if (snapshot == null) return null;

      final child = builder!(snapshot);
      final secondSlashIndex = path.substring(1).indexOf('/');

      // if the path ends at the child of this node
      if (secondSlashIndex == -1) {
        return (child.id == path.substring(1)) ? child : null;
      }

      return child.getChild(
        path: path.substring(secondSlashIndex + 1),
        parentPath: '$parentPath/$id',
        values: values,
      );
    case DynamicInputsNode():
    case InputsNode():
      final children = this is InputsNode
          ? (this as InputsNode).children
          : (values['$parentPath/$id'] as List<WoFormNode>?) ?? [];

      // if the path ends at the children of this node
      if (secondSlashIndex == -1) {
        return children.firstWhereOrNull((i) => i.id == path.substring(1));
      }

      return children
          .firstWhereOrNull(
            (i) => i.id == path.substring(1, secondSlashIndex + 1),
          )
          ?.getChild(
            path: path.substring(secondSlashIndex + 1),
            parentPath: '$parentPath/$id',
            values: values,
          );

    case LockerNode(:final child):
      // if the path ends at the child of this node
      if (secondSlashIndex == -1) {
        return (child.id == path.substring(1)) ? child : null;
      }

      return child.getChild(
        path: path.substring(secondSlashIndex + 1),
        parentPath: '$parentPath/$id',
        values: values,
      );

    case RootNode(:final children):
      assert(
        parentPath == '',
        'The parentPath of RootNode must always be an empty string.',
      );

      if (!path.startsWith('/')) {
        throw ArgumentError(
          'An input path must start with character "/" : $path',
        );
      }

      final secondSlashIndex = path.substring(1).indexOf('/');

      // if the path ends at the children of this node
      if (secondSlashIndex == -1) {
        return children.firstWhereOrNull((i) => i.id == path.substring(1));
      }

      final firstPathId = path.substring(1, secondSlashIndex + 1);

      return children
          .firstWhereOrNull(
            (
              child,
            ) => child.id == firstPathId,
          )
          ?.getChild(
            path: path.substring(secondSlashIndex + 1),
            // skip root's id
            parentPath: parentPath,
            values: values,
          );

    case PathBuilderNode():
    case SelectorNode():
    case ValueBuilderNode():
    case ValuesBuilderNode():
      final child = switch (this) {
        PathBuilderNode(:final builder) => builder!(
          '$parentPath/$id',
        ),
        SelectorNode(:final selector, :final builder) => builder!(
          selector!(values),
        ),
        ValueBuilderNode(:final path, :final builder) => builder!(
          values.getValue(path, parentPath: '$parentPath/$id'),
        ),
        ValuesBuilderNode(:final paths, :final builder) => builder!(
          {
            for (final path in paths)
              path: values.getValue(path, parentPath: '$parentPath/$id'),
          },
        ),
        _ => throw AssertionError(),
      };

      // if the path ends at the child of this node
      if (secondSlashIndex == -1) {
        return (child.id == path.substring(1)) ? child : null;
      }

      return child.getChild(
        path: path.substring(secondSlashIndex + 1),
        parentPath: '$parentPath/$id',
        values: values,
      );
    case ValueListenerNode():
    case WidgetNode():
    case EmptyNode():
      return null;

    // WoFormInput overrides this method
    case WoFormInput():
      throw UnimplementedError();
  }
}