content: // Copyright 2016 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  import 'package:meta/meta.dart';
  
  import '../utils.dart';
  import '../visitor/interface/value.dart';
  import '../value.dart';
  
  /// A SassScript list.
  ///
  /// {@category Value}
  @sealed
  class SassList extends Value {
    // TODO(nweiz): Use persistent data structures rather than copying here. An
    // RRB vector should fit our use-cases well.
    //
    // We may also want to fall back to a plain unmodifiable List for small lists
    // (<32 items?).
    final List<Value> _contents;
  
    // We don't use public fields because they'd be overridden by the getters of
    // the same name in the JS API.
  
    ListSeparator get separator => _separator;
    final ListSeparator _separator;
  
    bool get hasBrackets => _hasBrackets;
    final bool _hasBrackets;
  
    /// @nodoc
    @internal
    bool get isBlank =>
        !hasBrackets && asList.every((element) => element.isBlank);
  
    List<Value> get asList => _contents;
  
    /// @nodoc
    @internal
    int get lengthAsList => asList.length;
  
    /// Returns an empty list with the given [separator] and [brackets].
    ///
    /// The [separator] defaults to [ListSeparator.undecided], and [brackets] defaults to `false`.
    const SassList.empty({ListSeparator? separator, bool brackets = false})
        : _contents = const [],
          _separator = separator ?? ListSeparator.undecided,
          _hasBrackets = brackets;
  
    /// Returns an empty list with the given [separator] and [brackets].
    SassList(Iterable<Value> contents, this._separator, {bool brackets = false})
        : _contents = List.unmodifiable(contents),
          _hasBrackets = brackets {
      if (separator == ListSeparator.undecided && asList.length > 1) {
        throw ArgumentError(
          "A list with more than one element must have an explicit separator.",
        );
      }
    }
  
    /// Add parentheses to the debug information for lists to help make the list
    /// bounds clear.
    String toString() {
      if (hasBrackets ||
          lengthAsList == 0 ||
          (lengthAsList == 1 && separator == ListSeparator.comma)) {
        return super.toString();
      }
  
      return "(${super.toString()})";
    }
  
    /// @nodoc
    @internal
    T accept<T>(ValueVisitor<T> visitor) => visitor.visitList(this);
  
    SassMap assertMap([String? name]) =>
        asList.isEmpty ? const SassMap.empty() : super.assertMap(name);
  
    SassMap? tryMap() => asList.isEmpty ? const SassMap.empty() : null;
  
    bool operator ==(Object other) =>
        (other is SassList &&
            other.separator == separator &&
            other.hasBrackets == hasBrackets &&
            listEquals(other.asList, asList)) ||
        (asList.isEmpty && other is SassMap && other.asList.isEmpty);
  
    int get hashCode => listHash(asList);
  }
  
  /// An enum of list separator types.
  ///
  /// {@category Value}
  enum ListSeparator {
    /// A space-separated list.
    space('space', ' '),
  
    /// A comma-separated list.
    comma('comma', ','),
  
    /// A slash-separated list.
    slash('slash', '/'),
  
    /// A separator that hasn't yet been determined.
    ///
    /// Singleton lists and empty lists don't have separators defined. This means
    /// that list functions will prefer other lists' separators if possible.
    undecided('undecided', null);
  
    final String _name;
  
    /// The separator character.
    ///
    /// If the separator of a list has not been decided, this value will be
    /// `null`.
    final String? separator;
  
    const ListSeparator(this._name, this.separator);
  
    String toString() => _name;
  }
  