Type-safe codecs for dune build files
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

OCaml 95.3%
Dune 1.7%
Other 2.9%
2 1 0

Clone this repository

https://tangled.org/gazagnaire.org/ocaml-dune https://tangled.org/did:plc:jhift2vwcxhou52p3sewcrpx/ocaml-dune
git@git.recoil.org:gazagnaire.org/ocaml-dune git@git.recoil.org:did:plc:jhift2vwcxhou52p3sewcrpx/ocaml-dune

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

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.Filedune build files
  • Dune.Projectdune-project files
  • Dune.Workspacedune-workspace files
  • Dune.Package — installed dune-package metadata

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.