Build a study planner from two source files, test it, and run it. Then build a native executable and run it outside the source project.
Complete Failures and Resources first. You need topaz and a Rust toolchain for your platform. See First Program for installation.
Create the project
Run these commands from the folder where you want to keep the project.
topaz init --root study-plan
cd study-planinit creates topaz.toml and src/main.tpz. It refuses to overwrite an existing project. Choose a new folder name if study-plan already exists.
Inside study-plan, create a tests folder. Replace src/main.tpz and add the two files shown below using the code in the next section.
study-plan/
├── topaz.toml
├── src/
│ ├── main.tpz
│ └── plan.tpz
└── tests/
└── plan.tpzThe generated topaz.toml contains these project settings. This configuration file is called the manifest.
[package]
name = "study-plan"
version = "0.1.0"
language = "5.20"
entry = "src/main.tpz"
[build]
target = "native"
deterministic = true
[dependencies]
std = "5.20"entry selects the file to run. target = "native" selects an executable for your operating system. The manifest also records the package name, version, language version, and dependencies.
Add the source files and test
Save this code as src/plan.tpz. It creates tasks and adds up the unfinished work.
export record StudyTask {
title: string,
minutes: int,
done: bool = false,
}
function parseMinutes(text: string) -> Result<int, string> {
match toInt(text) {
case Some(value) if value > 0 => Ok(value)
case _ => Err("minutes must be a positive integer")
}
}
export function makeTask(
title: string,
minuteText: string,
done: bool = false,
) -> Result<StudyTask, string> {
let minutes = parseMinutes(minuteText)?
Ok(StudyTask { title: title, minutes: minutes, done: done })
}
export function summarize(tasks: Array<StudyTask>) -> string {
let mut remaining = 0
for task in tasks {
if !task.done {
remaining = remaining + task.minutes
}
}
"tasks={tasks.length}, remaining={remaining} minutes"
}Each source file is a module. export makes a declaration available to other modules. parseMinutes has no export, so other files use makeTask to create a task.
Replace src/main.tpz with the following code.
import src.plan { StudyTask, makeTask, summarize }
export function main(args: Array<string>, stdin: string) -> Result<int, string> {
let tasks: Array<StudyTask> = [
makeTask("Run first program", "10", true)?,
makeTask("Build application", "25", false)?,
]
print(summarize(tasks))
Ok(0)
}import src.plan brings the exported names from src/plan.tpz into this file. main creates two tasks and prints their summary. It accepts command-line arguments and standard input, although this example does not use them. Ok(0) returns a successful exit code.
Save this test as tests/plan.tpz.
import src.plan { StudyTask, makeTask, summarize }
match makeTask("Read syntax map", "15") {
case Ok(task) => assert(task.minutes == 15, "valid minutes")
case Err(message) => assert(false, message)
}
match makeTask("Read syntax map", "later") {
case Ok(_) => assert(false, "invalid minutes were accepted")
case Err(message) => assert(
message == "minutes must be a positive integer",
"invalid minutes",
)
}
let tasks: Array<StudyTask> = [
StudyTask { title: "Run first program", minutes: 10, done: true },
StudyTask { title: "Build application", minutes: 25 },
]
assert(
summarize(tasks) == "tasks=2, remaining=25 minutes",
"summary",
)The test accepts "15", rejects "later", and checks the summary. topaz test provides assert for these checks.
Lock, check, test, and run
Run the following commands from study-plan. First, create the dependency lock file.
topaz lock --root .The command creates topaz.lock with this content.
[[package]]
name = "study-plan"
version = "0.1.0"
source = "root"
manifest_hash = "sha256:780ff549a9fb9b09d27c62df8ce48e70c1fc7b33d6d429e6bb826685f42e7615"topaz.lock records the resolved package settings and a hash of the manifest. Commands with --locked check that the lock file matches the manifest. After changing topaz.toml, run topaz lock --root . again.
Check formatting and types, then run the test.
topaz fmt --check --root .
topaz check --root . --locked
topaz test tests/plan.tpz --root . --lockedfmt --check leaves files unchanged. check checks the entry file and its imported modules. Both commands exit successfully when their checks pass. The test prints the following line.
tests/plan.tpz: test-okIf formatting differs, run topaz fmt --root . to format the files, then repeat the checks. For a type or name error, fix the reported line. If a test fails, compare its input and expected result in tests/plan.tpz with the functions in src/plan.tpz.
Run the application after the checks pass.
topaz run --root . --lockedThe first task is finished, so the remaining time comes from the second task.
tasks=2, remaining=25 minutesBuild and run outside the project
Build the native executable from the same project folder.
topaz build --release --root . --locked --out-dir ../study-plan-productThe build uses your platform's Rust toolchain and writes the result to ../study-plan-product.
study-plan-product/
├── GENERATED-OUTPUT-NOTICE.txt
├── LICENSE
├── NOTICE
├── target
│ └── release
│ └── program[.exe]
└── topaz-artifact.jsonprogram[.exe] means program on Unix and program.exe on Windows. The executable runs without Rust or Topaz. Keep the metadata and legal notices with it when copying the product folder.
If the build reports a missing Rust toolchain, install it for your platform. If the output folder contains a different target or modified generated files, keep that folder and choose a new empty output folder.
Leave the project folder, rename it, and run the executable from the product folder. Choose another name if source-unavailable already exists. Renaming keeps your source files for later work.
cd ..
mv study-plan source-unavailable
cd study-plan-product
./target/release/programIn PowerShell, use Rename-Item study-plan source-unavailable for the rename and .\target\release\program.exe to run. The program prints the same result.
tasks=2, remaining=25 minutesYou have now run the built program from a folder with no .tpz source files. The source remains in source-unavailable.
Try this
In source-unavailable/src/main.tpz, change the second task's minute text from "25" to "30". From source-unavailable, repeat the checks, test, run, and build commands above. Then run the rebuilt executable from study-plan-product. What changes?
Answer
The application and rebuilt executable print tasks=2, remaining=30 minutes. The test still passes. It creates its own 25-minute task in tests/plan.tpz, so its expected summary stays unchanged.
Ready to continue when
The checks and test pass, and both the application and the executable print the expected summary. Run the executable from outside the source project to finish the exercise.
Keep Application Loop handy for the command sequence. Continue with Reading a Real File, or consult Modules & Visibility and Artifacts & Deployment. Syntax at a Glance is the syntax reference.