Your first Incan project¶
This tutorial walks you through setting up a real Incan project from scratch — from incan init
to a running, tested program with multiple modules.
Prerequisites: Getting started (Incan installed and incan run hello.incn works).
Time: ~10 minutes.
What you'll build¶
A small command-line tool called greeter with a greeting module and tests. Along the way you'll:
- Scaffold a project with
incan init - Split code into modules with imports
- Write and run tests
Step 1: Create the project¶
mkdir greeter && cd greeter
incan init
incan init scaffolds a ready-to-run project:
Created project 'greeter' at .
src/main.incn Entry point
tests/test_main.incn Starter test
incan.toml Project manifest
Run it: incan run src/main.incn
Test it: incan test tests/
Your project layout:
greeter/
├── src/
│ └── main.incn # "Hello from greeter!"
├── tests/
│ └── test_main.incn # Placeholder test
└── incan.toml # Manifest with [project.scripts] main set
Try it immediately:
incan run src/main.incn
Hello from greeter!
The generated incan.toml already has [project.scripts] main pointing at src/main.incn, so commands like incan lock
will work without a file argument later on.
Step 2: Add a module¶
Let's extract the greeting logic into its own module. Create src/greet.incn:
"""Greeting utilities."""
pub def greet(name: str) -> str:
return f"Hello, {name}!"
Note the pub keyword — without it, greet would be private to its module and you couldn't
import it.
Now update src/main.incn to use the greet function from the greet.incn module:
from greet import greet
def main() -> None:
println(greet("World"))
Run again:
incan run src/main.incn
output:
Hello, World!
Adding more functions¶
Let's add a second function. Update src/greet.incn to add the farewell function:
"""Greeting utilities."""
pub def greet(name: str) -> str:
return f"Hello, {name}!"
pub def farewell(name: str) -> str:
return f"Goodbye, {name}!"
And update src/main.incn to use both:
from greet import greet, farewell
def main() -> None:
println(greet("World"))
println(farewell("World"))
incan run src/main.incn
output:
Hello, World!
Goodbye, World!
Step 3: Write tests¶
incan init already created a placeholder test. Let's replace it with real tests for our
greeting module. Update tests/test_main.incn:
from greet import greet, farewell
from std.testing import assert_eq
def test_greet() -> None:
assert_eq(greet("Alice"), "Hello, Alice!")
def test_greet_empty() -> None:
assert_eq(greet(""), "Hello, !")
def test_farewell() -> None:
assert_eq(farewell("Alice"), "Goodbye, Alice!")
Notice the import: from greet import greet, farewell — the exact same syntax as in
src/main.incn. The test runner resolves imports against your project's source root
(src/), so tests and source code share the same import paths.
Run the tests:
incan test tests/
You should see output like:
=================== test session starts ===================
collected 3 item(s)
test_main.incn::test_greet PASSED
test_main.incn::test_greet_empty PASSED
test_main.incn::test_farewell PASSED
=================== 3 passed in 2.69s ===================
Test discovery
Test files are found by name (test_*.incn) and test functions by name (def test_*()).
See: Testing.
Your final project layout¶
greeter/
├── src/
│ ├── main.incn # Entry point
│ └── greet.incn # Greeting module
├── tests/
│ └── test_main.incn # Tests for greet module
└── incan.toml # Project manifest
Recap¶
| Step | What you did | Key command / concept |
|---|---|---|
| 1 | Scaffolded a project | incan init |
| 2 | Split code into modules | pub, from ... import ... |
| 3 | Wrote and ran tests | incan test tests/ |
Next steps¶
- Rust interop — Use Rust crates from Incan code
- Managing dependencies —
incan.toml, version annotations, and lock files - Project configuration reference — Full
incan.tomlformat - CI & automation — Locked builds, pipelines, and deployment
- The Incan Book — Learn the language itself