A set of libraries providing functionality to the Bettongia projects
Features
-
package:betto_common/collections.dart:Stack: a generic stack data structureRange: represents a range of numbers with a step sizeIterableChecks: extension inIterablewith matching functionsMappedObjectprovides a small interface for objects with atoMapmethod
-
package:betto_common/result.dart:a result type that can represent aSuccessor aFailure -
package:betto_common/string.dartIntlStringis used to map a String to a Locale.IntlStringslets you manage a set of strings that generally mean the same thing but in different locales.StringExtensionprovides functions primarily to help with parsing.StringListis a container for a string or a list of strings.
Getting started
The libraries in this package are self-contained and should be easily added to your codebase.
Usage
The examples directory (examples/) has a number of ready-to-try demo files.
Collection
Stack
final stack = Stack<int>();
// Push 3 numbers onto the stack
stack.push(1);
stack.push(2);
stack.push(3);
// Print the stack
print(stack.toList());
// Pop 1 number off the stack
print('Pop: ${stack.pop()}');
// Print the stack
print(stack.toList());
Result:
[1, 2, 3]
Pop: 3
[1, 2]
Range
A Range object can be used to iterate over a range of numbers.
Using the range() function can be a useful shorthand:
for (var i in range(start: 1, stop: 5)) {
print(i);
}
Output:
1
2
3
4
Iterable checks
Use hasTheSameElements to check if two lists have the same elements:
void main() {
List<int> list1 = [1, 2, 3, 4, 5];
List<int> list2 = [1, 5, 4, 2, 3];
List<int> list3 = [1, 2, 3, 5, 5];
print(
'$list1 and $list2 have the same elements: '
'${list1.hasTheSameElements(list2)}',
);
print(
'$list1 and $list3 have the same elements: '
'${list1.hasTheSameElements(list3)}',
);
}
Output:
[1, 2, 3, 4, 5] and [1, 5, 4, 2, 3] have the same elements: true
[1, 2, 3, 4, 5] and [1, 2, 3, 5, 5] have the same elements: false
Use isSubList to check if one list is a sub-list of another:
void main() {
List<String> list1 = ['a', 'b', 'c'];
List<String> list2 = ['a', 'b', 'c', 'd'];
List<String> empty = [];
print(
'$list1 is a sublist of $list2: '
'${list1.isSubList(list2)}',
);
print(
'$list2 is a sublist of $list1: '
'${list2.isSubList(list1)}',
);
// The empty list is a subset of all lists.
print(
'$empty is a sublist of $list1: '
'${empty.isSubList(list1)}',
);
}
Output:
[a, b, c] is a sublist of [a, b, c, d]: true
[a, b, c, d] is a sublist of [a, b, c]: false
[] is a sublist of [a, b, c]: true
MappedObject
Mapped objects indicate that they can be converted to a map.
You'll note in the example that MappedObject<String> is used to indicate that
the map values will all be Strings.
class Person implements MappedObject<String> {
final String name;
final String address;
Person(this.name, this.address);
@override
Map<String, String> toMap() => {'name': name, 'address': address};
}
void main() {
final person = Person('Fred', '123 Main St.');
print(person.toMap());
}
Output:
{name: Fred, address: 123 Main St.}
You can drop the generic type declaration and have the mapped values by
dynamic:
class Person implements MappedObject {
final String name;
final int age;
Person(this.name, this.age);
@override
Map<String, dynamic> toMap() => {
'name': name,
'age': age,
};
}
void main() {
final person = Person('Fred', 42);
print(person.toMap());
}
Output:
{name: Fred, age: 42}
String
IntlString wraps a String and provides the Locale relevant to the String. We
can then group the strings in an IntlStrings to help us select a useful
variant from the set.
void main() {
final locales = {
'en': Locale.fromSubtags(languageCode: 'en'),
'au': Locale.fromSubtags(languageCode: 'en', countryCode: 'AU'),
'nz': Locale.fromSubtags(languageCode: 'en', countryCode: 'NZ'),
'us': Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
'de': Locale.fromSubtags(languageCode: 'de'),
'ja': Locale.fromSubtags(languageCode: 'ja', countryCode: 'JP'),
};
// Note that we don't have translations for each locale
final strings = IntlStrings([
IntlString('hello', locale: locales['en']),
IntlString("g'day", locale: locales['au']),
IntlString('hallo', locale: locales['de']),
], defaultLocale: locales['au']);
print('Default: ${strings.getString().value}');
print('NZ: ${strings.getString(locale: locales['nz']).value}');
print('DE: ${strings.getString(locale: locales['de']).value}');
print('JA: ${strings.getString(locale: locales['ja']).value}');
}
Output:
Default: {value: g'day, locale: en-AU}
NZ: {value: hello, locale: en}
DE: {value: hallo, locale: de}
JA: {value: hello, locale: en}
The request for New Zealand (NZ) falls back to en; German is covered by de;
There's nothing defined for ja-JP or ja so the default is used.
String extension
StringExtension extends String with a few handy checks and functions.
cutFirst cuts the first part of a String before the requests separator:
void main() {
var (before, after, found) = 'Hello, world!'.cutFirst(',');
print('Before: $before');
print('After: $after');
print('Found: $found');
}
Output:
Before: Hello
After: world!
Found: true
Testing
To run the full test suite, generate docs etc:
make
Linux container
The Containerfile can be used to test the package on Linux.
podman build -t betto-common-cicd .
podman run --rm betto-common-cicd
Additional information
None really - it's just a portmanteau of utility items.
Libraries
- collections
- Collection-oriented classes and extensions
- string
- A small library of String-related goodies.