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.
//

#[base]
type Base {
    fn isBase(): bool {
        return true;
    }
}
type Extends extends Base {
    fn isBase(): bool {
        return false;
    }
}

return: {
    obj empty: {}
    fn create_base(): Base {
        return self.empty;
    }

    #[test]
    fn cast_on_return() {
        let base = self.create_base();
        assertEq(typename base, 'Base');
        assert(base.isBase());
    }
}

as: {
    obj as_cast: {}

    #[test]
    fn cast_on_as() {
        let as_cast = self.as_cast as Base;
        assertEq(typename as_cast, 'Base');
        assert(as_cast.instanceOf('Base'));
        assert(as_cast.isBase());
    }
}

poly: {
    Extends extends_obj: {}
    fn get_base(object: obj): Base {
        return object;
    }

    #[test]
    fn instance_of_catch() {
        assertEq(typename self.extends_obj, 'Extends');

        let object = self.get_base(self.extends_obj);
        assertEq(typename object, 'Extends');
        assert(object.instanceOf('Extends'));
        assert(object.instanceOf('Base'));

        assertNot(object.isBase());
        assert(object.Base::isBase());
    }
}

upcast: {
    obj empty: {}

    #[test]
    fn upcast_extends() {
        let object = self.empty as Extends;
        assertNot(object.isBase());
        assertEq(typename object, 'Extends');
        assert(object.instanceOf('Extends'));
        assert(object.instanceOf('Base'));

        object = object as Base; // Already of type Base...
        assertNot(object.isBase());
        assertEq(typename object, 'Extends');
        assert(object.instanceOf('Extends'));
        assert(object.instanceOf('Base'));

        assert(object.upcast());
        assert(object.isBase());
        assertEq(typename object, 'Base');
        assert(object.instanceOf('Base'));
        assertNot(object.instanceOf('Extends'));

        assertNot(object.upcast()); // Nowhere to upcast to...

        assert(object.instanceOf('Base'));
        assert(object.prototype() != null);

        let base_attributes = object.prototypeAttributes();
        assertEq(base_attributes, map([('base', null)]));

        assert(object.removePrototype());
        assertNot(object.instanceOf('Base'));
    }
}

errors: {
    type NoDefaults {
        x: int;
        y: int;
    }

    #[test]
    #[errors]
    fn no_defaults() {
        let inst = new NoDefaults {}; // does not have an x or y defined, so error
    }

    #[test]
    fn has_defaults() {
        let inst = new {
            x: 4;
            y: 5;
        };
        inst = inst as NoDefaults;
        assertEq(inst.x, 4);
        assertEq(inst.y, 5);
    }

    #[test]
    #[errors]
    fn wrong_types() {
        let inst = new NoDefaults {
            x: 'hi'
            y: 5
        }; // Errors because of wrong types!
    }
}

default_values: {
    type Defaults {
        x: float = 20cm;
        y: float = 2m;
        z: float = 10mm;
    }
    type ExtendsDefaults extends Defaults {
        q: int = 2;
    }

    #[test]
    fn create_defaults() {
        let inst = new Defaults {};
        assertEq(inst.x, 20cm);
        assertEq(inst.y, 2m);
        assertEq(inst.z, 10mm);
    }

    #[test]
    fn cast_defaults() {
        let inst = new {};
        inst = inst as Defaults;
        assertEq(inst.x, 20cm);
        assertEq(inst.y, 2m);
        assertEq(inst.z, 10mm);
    }

    #[test]
    fn extends_cast_defaults() {
        let inst = new {};
        inst = inst as ExtendsDefaults;
        assertEq(inst.x, 20cm);
        assertEq(inst.y, 2m);
        assertEq(inst.z, 10mm);
        assertEq(inst.q, 2);
    }

    #[test]
    fn downcast_defaults() {
        let inst = new Defaults {};
        inst = inst as ExtendsDefaults;
        assertEq(inst.x, 20cm);
        assertEq(inst.y, 2m);
        assertEq(inst.z, 10mm);
        assertEq(inst.q, 2);
    }

    empty: {}
    #[test]
    fn field_cast() {
        let inst = self.empty as ExtendsDefaults;
        assertEq(self.empty.x, 20cm);
        assertEq(self.empty.y, 2m);
        assertEq(self.empty.z, 10mm);
        assertEq(self.empty.q, 2);
    }
}