stof 0.3.24

Stof is a unified data interface and interchange format for creating, sharing, and manipulating data. Stof removes the fragile and cumbersome parts of combining and using data in applications.
Documentation
//
// Copyright 2024 Formata, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//


#[test(['hi'])]
fn push(): vec {
    let array = [];
    array.push('hi');
    return array;
}

#[test]
fn append() {
    let a = [1, 2, 3, 4, 5];
    let b = [6, 7, 8, 9];
    a.append(b);
    assertEq(a, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
    assertEq(b, [6, 7, 8, 9]);
}

#[test]
fn append_boxed() {
    let a = [1, 2, 3, 4, 5];
    let b = box([6, 7, 8, 9]);
    a.append(b);
    assertEq(a, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
    assertEq(b, []);
}

#[test(['hi'])]
fn pop(): vec {
    let array = ['hi', 'there'];
    assertEq(array.pop(), 'there');
    return array;
}

#[test([1, 2, 4, 5, 6])]
fn pop_at(): vec {
    let array = [1, 2, 3, 4, 5, 6];
    assertEq(array.pop(2), 3); // if given a number param, it is treated as an index
    return array;
}

#[test(['a', 'c', 'd', 'e'])]
fn pop_val(): vec {
    let array: vec = ['a', 'b', 'c', 'd', 'e'];
    assertEq(array.pop('b'), 'b');
    return array;
}

#[test]
fn reverse() {
    let array = [1, 2, 3, 4];
    array.reverse();
    assertEq(array, [4, 3, 2, 1]);
}

#[test]
fn reversed() {
    let array = [1, 2, 3, 4];
    let reversed = array.reversed();

    assertEq(array, [1, 2, 3, 4]);
    assertEq(reversed, [4, 3, 2, 1]);
}

#[test]
fn len() {
    let array = [1, 2, 3, 4, 5, 6, 7, 8];
    assertEq(array.len(), 8);
}

#[test]
fn empty() {
    let array = [];
    assert(array.empty());

    array.push(1);
    assertNot(array.empty());
}

#[test]
fn any() {
    let array = [];
    assertNot(array.any());

    array.push(1);
    assert(array.any());
}

#[test]
fn at() {
    let array = [1, 2, 3, 4];

    assertEq(array.at(2), 3);
    assertEq(array[3], 4);
}

#[test]
fn nested_at() {
    let array = [[1, 2, 3], [4, 5, 6]];
    assertEq(array[0][1], 2);
    assertEq(array.at(1).at(2), 6);
}

#[test(1)]
fn first(): int {
    let array = [1, 2, 3];
    return array.first();
}

#[test(3)]
fn last(): int {
    let array = [1, 2, 3];
    return array.last();
}

#[test('hello, world')]
fn join(): str {
    return ['hello', 'world'].join(', ');
}

#[test]
fn has() {
    let array = ['hello', 'world'];
    assert(array.has('hello'));
    assertNot(array.has('dne'));

    assert(array.contains('hello'));
    assertNot(array.contains('dne'));
}

#[test]
fn find() {
    let array = ['hello', 'world', 5];
    assertEq(array.find(5), 2);
    assertEq(array.find('world'), 1);
    assertEq(array.find('dne'), -1);
    assertEq(array.find(2), -1);
}

#[test]
fn remove_first_occurence() {
    let array = ['hello', 'world', 5, 4, 'world'];
    
    assertEq(array.remove('dne'), null);
    assertEq(array.remove(5), 5);
    assertEq(array.remove('world'), 'world');
    assertEq(array, ['hello', 4, 'world']);
}

#[test]
fn remove_last_occurence() {
    let array = ['hello', 'world', 4, 'world'];
    assertEq(array.removeLast('world'), 'world');
    assertEq(array, ['hello', 'world', 4]);
}

#[test]
fn remove_all() {
    let array = [1, 1, 1, 1, 2, 1, 1, 1, 1];
    assertEq(array.removeAll(1).len(), 8);
    assertEq(array, [2]);
    assert(array.removeAll(1).empty());
}

#[test]
fn insert() {
    let array = [1, 2, 3, 4, 5, 6];
    array.insert(2, 'hello');
    assertEq(array, [1, 2, 'hello', 3, 4, 5, 6]);
}

#[test]
fn set() {
    let array = [1, 2, 3, 4, 5, 6];
    array.set(2, 'hello');
    assertEq(array, [1, 2, 'hello', 4, 5, 6]);

    array.replace(4, 42);
    assertEq(array, [1, 2, 'hello', 4, 42, 6]);
}

#[test]
fn iter() {
    let array = 0..15;
    array.iter((val: int): int => {
        if (val % 2 == 0) {
            return val + 1;
        }
        return null; // don't set anything
    });
    assertEq(array, [1, 1, 3, 3, 5, 5, 7, 7, 9, 9, 11, 11, 13, 13, 15]);
}

#[test]
fn retain_numbers() {
    let array = [1, 2, 3, 4, 'hi', 9, 'dude', true, {}, 23];
    array.retain((val: int): bool => true); // only callable for numbers
    assertEq(array, [1, 2, 3, 4, 9, true, 23]); // booleans can cast to numbers
}

#[test]
fn retain_evens() {
    let array = 0..100;
    array.retain((v: int): int => v % 2 == 0);
    assertEq(array, 0..100|2);
}

#[test([0, 1, 2, 2, 4, 5, 5, 7, 8])]
fn sort(): vec {
    let array = [5, 2, 4, 5, 7, 8, 0, 2, 1];
    array.sort();
    return array;
}

#[test([2, 3, 5, 6, 6])]
fn sort_by(): vec {
    let array = [new {k:6}, new {k:3}, new {k:5}, new {k:2}, new {k:6}];
    array.sort((a: obj, b: obj): int => {
        if (a.k < b.k) return -1;
        if (a.k > b.k) return 1;
        return 0;
    });
    let res = [];
    for (object in array) {
        res.push(object.k);
    }
    return res;
}

boxed_values: {
    #[test([2, 3, 4, 4, 'hi', true, 33])]
    fn boxed_array(): vec {
        let array = box([1, 1, 2, 3, 4, 4]);
        assertEq(array.removeAll(1), [1, 1]);
        array.push('hi', true, 33);
        return array;
    }

    #[test]
    fn boxed_array_value() {
        let array = [1, box(42)];

        // Mutate the value by reference
        {
            let val = array.at(1);
            assertEq(val, 42);

            val += 10;
            assertEq(val, 52);
        }
        {
            let val = array.at(1);
            assertEq(val, 52);
        }

        // Unbox the value with helper
        {
            let val = unbox(array.at(1));
            val += 10;
            assertEq(val, 62);
        }
        {
            let val = array.at(1);
            assertEq(val, 52);
        }

        // Unbox the value with a cast
        {
            let val = array.at(1) as int; // unboxed via cast
            val += 10;
            assertEq(val, 62);
        }
        {
            let val = array.at(1);
            assertEq(val, 52);
        }

        // Push a boxed value by cast
        array.push(11 as Box<float>);
        let val = array.at(2);
        val += 10;
        assertEq(val, 21);
        assertEq(array.at(2), 21);
    }
}