nox-dune#
Type-safe codecs for the dune-format file family, built on nox-sexp.
Each file kind is its own self-contained module:
Dune.File—dunebuild filesDune.Project—dune-projectfilesDune.Workspace—dune-workspacefilesDune.Package— installeddune-packagemetadata
Every module exposes the same six-verb IO shape — of_string,
of_string_exn, to_string, of_reader, of_reader_exn,
to_writer — plus typed accessors over an abstract t.
Installation#
Install with opam:
opam install nox-dune
If opam cannot find the package, it may not yet be released in the
public opam-repository. Add the overlay repository, then install:
opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
opam update
opam install nox-dune
Quick start#
dune build files#
let dune_text = {|
(library (name foo) (libraries fmt))
(library (name bar))
(executable (name main))
(test (name test_foo) (libraries alcotest))
|}
let file = Dune.File.of_string_exn dune_text
# Dune.File.library_names file
- : string list = ["foo"; "bar"]
# Dune.File.private_library_names file
- : string list = ["foo"; "bar"]
# Dune.File.executable_names file
- : string list = ["main"]
# Dune.File.test_names file
- : string list = ["test_foo"]
dune-project files#
let project_text = {|
(lang dune 3.17)
(name my-project)
(version 1.0.0)
(license ISC)
(authors "Alice" "Bob")
(source (github owner/repo))
(package
(name my-project)
(synopsis "A demo")
(depends ocaml dune (alcotest :with-test)))
|}
let proj = Dune.Project.of_string_exn project_text
# Dune.Project.name proj
- : string option = Some "my-project"
# Dune.Project.license proj
- : string option = Some "ISC"
# Dune.Project.authors proj
- : string list = ["Alice"; "Bob"]
# Dune.Project.source proj
- : string option = Some "github:owner/repo"
# List.map Dune.Project.Package.name (Dune.Project.packages proj)
- : string list = ["my-project"]
# List.map Dune.Project.Package.depends (Dune.Project.packages proj)
- : string list list = [["ocaml"; "dune"; "alcotest"]]
Dune.Project.make builds a value from typed fields:
let proj =
Dune.Project.make
~dune_version:"3.17"
~name:"demo"
~version:"0.1"
~license:"ISC"
~authors:["Alice"]
()
dune-workspace files#
let ws =
Dune.Workspace.of_string_exn
{|(lang dune 3.17) (context default)|}
# Dune.Workspace.lang ws
- : string * string = ("dune", "3.17")
# List.length (Dune.Workspace.contexts ws)
- : int = 1
dune-package metadata#
Dune.Package.libraries decodes every (library ...) stanza in an
installed _opam/lib/<pkg>/dune-package file, skipping the
(lang ...) header:
let dune_package_text = {|
(lang dune 3.0)
(library
(name helix.jx)
(kind virtual)
(main_module_name Jx))
(library
(name helix.jx.jsoo)
(kind normal)
(implements helix.jx)
(main_module_name Jx)
(modules
(wrapped
(group
(alias (obj_name jx__jx_jsoo__))
(name Jx)
(modules
(module (obj_name jx__Jx_ffi)))))))
|}
let libs = Dune.Package.libraries dune_package_text
# List.map Dune.Package.name libs
- : string list = ["helix.jx"; "helix.jx.jsoo"]
# Dune.Package.implements (List.nth libs 1)
- : string option = Some "helix.jx"
# List.sort String.compare (Dune.Package.modules (List.nth libs 1))
- : string list = ["Jx"; "Jx__Jx_ffi"]
IO entry points#
The same six verbs appear on every file-kind module:
# #show Dune.File.of_string
val of_string : string -> (Dune.File.t, Sexp.Error.t) result
# #show Dune.File.to_string
val to_string : ?indent:int -> ?preserve:bool -> Dune.File.t -> string
Omit ?indent for compact output (one stanza per line, single-space
atoms). Pass ~indent:n to wrap multi-field stanzas with n spaces
per nesting level — ~indent:1 matches dune's own layout:
# print_endline (Dune.File.to_string file)
(library (name foo) (libraries fmt))
(library (name bar))
(executable (name main))
(test (name test_foo) (libraries alcotest))
- : unit = ()
# print_endline (Dune.File.to_string ~indent:1 file)
(library
(name foo)
(libraries fmt))
(library (name bar))
(executable (name main))
(test
(name test_foo)
(libraries alcotest))
- : unit = ()
For streaming I/O, every module also has of_reader /
of_reader_exn / to_writer over Bytesrw.Bytes.Reader.t /
Bytesrw.Bytes.Writer.t.
Variables#
Dune embeds %{name} and %{name:default} references in atoms.
Dune.Var parses and expands them:
# Dune.Var.to_string (Dune.Var.v "name")
- : string = "%{name}"
# Dune.Var.to_string (Dune.Var.v ~default:"foo" "name")
- : string = "%{name:foo}"
# let env = function "name" -> Some "Alice" | _ -> None
val env : string -> string option = <fun>
# Dune.expand_atom ~env "Hello %{name}!"
- : string = "Hello Alice!"
# Dune.has_variables "no vars here"
- : bool = false
License#
ISC.