Projects and libraries

One file is a complete loft program, and for a long time that is all you need. When you want to share code between programs, or keep tests beside it, you turn the folder into a package.

What a package looks like

A package is a folder with a loft.toml file in it and two sub-folders:

  greeter/
    loft.toml          what this package is called
    src/greeter.loft   the code
    tests/greet.loft   the tests

tests is where your tests live and the name matters — loft test looks in exactly that folder, and a test file anywhere else is not found. src is a habit rather than a rule: the manifest below names the entry file, and src/<name>.loft is only what loft assumes when nothing says otherwise.

The manifest

loft.toml names the package and says which file is its front door:

  [package]
  name    = "greeter"
  version = "0.1.0"
  [library]
  entry = "src/greeter.loft"

The entry file is the one that other code sees. Anything you want to share from it is marked pub:

  pub fn greet(who: text) -> text { "hello, {who}!" }

Without pub a function is not private — it is un-imported. pub is what lets a caller write the bare name; everything else in the entry file stays reachable as greeter::name. That is the difference between "you may use this" and "you cannot see this", and loft only offers the first.

Testing a package

Inside the package folder, loft test finds and runs everything in tests:

  $ cd greeter && loft test
  test result: ok. 1 passed; 1 file

The tests reach your code by importing the package by name:

  use greeter::*;
  fn test_greet_names_the_person() {
    assert(greet("Ada") == "hello, Ada!", "greet builds the sentence");
  }

use greeter::*; brings in every pub name, which is why greet("Ada") works bare. A bare use greeter; brings in only the prefix, so there you would write greeter::greet("Ada"). The selective forms give you a say in WHICH names arrive: use sums::add takes one name from a package, and use sums::(add, subtract) takes a group. Taking only what you name is the stronger position, because a package that later GROWS a name cannot then collide with one of yours.

The greeter:: prefix is available whichever form you wrote, and it is the spelling that says where a name came from — useful once you import several packages, and the only way to reach something the package did not mark pub.

Running one test while you work

Give loft test the file, or the file and one function:

  $ cd greeter && loft test greet
  test result: ok. 1 passed; 1 file

Check it compiles, without running anything

Inside the package, loft check compiles everything and reports problems:

  $ cd greeter && loft check
  loft build: src/greeter.loft → target(s): native
  loft build: building `native` (Native) …
  ok
  loft build: `native` ✓

A package that is only a library has no program to start, so there is nothing to run — check still tells you the code compiles, which is what you wanted to know.

Coverage — what the tests did not reach

Just above its result line, loft test tells you which of your functions no test ever entered. The sums package next door has two functions and a test for one of them, so it names the other:

  $ cd sums && loft test
  coverage: 1 of 2 functions were never entered by these tests
    src/sums.loft:3  subtract

When nothing is missing it says so in one line instead:

  $ cd greeter && loft test
  coverage: all 1 functions were entered by these tests

It is a list, never a percentage and never a gate. A percentage becomes a target, and tests written to move a number check nothing.

Using someone else's package

Add it to the manifest under [dependencies] and install:

  [dependencies]
  math = ">=0.1"
  loft install

Then use math; in your code. loft install also writes a lock file that records the exact versions, so the same code builds the same way later.

fn main() {

The package these examples describe is greeter, and this is its one function — a pub fn in src/greeter.loft that its tests import.

  greeting = "hello, Ada!";
  assert(greeting == "hello, Ada!", "what greet(\"Ada\") returns");
  println(greeting);
}