My agentic slop goes here. Not intended for anyone else!
README.md

Sortal - Username to Metadata Mapping Library#

Sortal is an OCaml library that provides a system for mapping usernames to various metadata including URLs, emails, ORCID identifiers, and social media handles. It uses the XDG Base Directory Specification for storage locations and jsont for JSON encoding/decoding.

Features#

  • XDG-compliant storage: Contact metadata stored in standard XDG data directories
  • JSON format: Type-safe JSON encoding/decoding using jsont
  • Rich metadata: Support for multiple names, email, social media handles (GitHub, Twitter, Bluesky, Mastodon), ORCID, URLs, and Atom feeds
  • Simple API: Easy-to-use functions for saving, loading, searching, and deleting contacts

Metadata Fields#

Each contact can include:

  • handle: Unique identifier/username (required)
  • names: List of full names with primary name first (required)
  • email: Email address
  • icon: Avatar/icon URL
  • thumbnail: Path to a local thumbnail image file
  • github: GitHub username
  • twitter: Twitter/X username
  • bluesky: Bluesky handle
  • mastodon: Mastodon handle (with instance)
  • orcid: ORCID identifier
  • url: Personal/professional website
  • atom_feeds: List of Atom/RSS feed URLs

Storage#

Contact data is stored as individual JSON files in the XDG data directory:

  • Default location: $HOME/.local/share/{app_name}/
  • Override with: ${APP_NAME}_DATA_DIR or XDG_DATA_HOME
  • Each contact stored as: {handle}.json

Usage Example#

Basic Usage#

(* Create a contact store from filesystem *)
let store = Sortal.create env#fs "myapp" in

(* Or create from an existing XDG context (recommended when using eiocmd) *)
let store = Sortal.create_from_xdg xdg in

(* Create a new contact *)
let contact = Sortal.Contact.make
  ~handle:"avsm"
  ~names:["Anil Madhavapeddy"]
  ~email:"anil@recoil.org"
  ~github:"avsm"
  ~orcid:"0000-0002-7890-1234"
  () in

(* Save the contact *)
Sortal.save store contact;

(* Lookup by handle *)
match Sortal.lookup store "avsm" with
| Some c -> Printf.printf "Found: %s\n" (Sortal.Contact.name c)
| None -> Printf.printf "Not found\n"

(* Search for contacts by name *)
let matches = Sortal.search_all store "Anil" in
List.iter (fun c ->
  Printf.printf "%s: %s\n"
    (Sortal.Contact.handle c)
    (Sortal.Contact.name c)
) matches

(* List all contacts *)
let all_contacts = Sortal.list store in
List.iter (fun c ->
  Printf.printf "%s: %s\n"
    (Sortal.Contact.handle c)
    (Sortal.Contact.name c)
) all_contacts

Integration with Eiocmd (for CLI applications)#

open Cmdliner

let my_command env xdg profile =
  (* Create store from XDG context *)
  let store = Sortal.create_from_xdg xdg in

  (* Search for a contact *)
  let matches = Sortal.search_all store "John" in
  List.iter (fun c ->
    match Sortal.Contact.best_url c with
    | Some url -> Logs.app (fun m -> m "%s: %s" (Sortal.Contact.name c) url)
    | None -> ()
  ) matches;
  0

(* Use Sortal's built-in commands *)
let () =
  let info = Cmd.info "myapp" in
  let my_cmd = Eiocmd.run ~info ~app_name:"myapp" ~service:"myapp"
    Term.(const my_command) in

  (* Include sortal commands as subcommands *)
  let list_contacts = Eiocmd.run ~use_keyeio:false
    ~info:Sortal.Cmd.list_info ~app_name:"myapp" ~service:"myapp"
    Term.(const (fun () -> Sortal.Cmd.list_cmd ()) $ const ()) in

  let cmd = Cmd.group info [my_cmd; list_contacts] in
  exit (Cmd.eval' cmd)

Design Inspiration#

The contact metadata structure is inspired by the Contact module from Bushel, adapted to use JSON instead of YAML and stored in XDG-compliant locations.

Dependencies#

  • eio: For effect-based I/O
  • xdge: For XDG Base Directory Specification support
  • jsont: For type-safe JSON encoding/decoding
  • fmt: For pretty printing

API Features#

The library provides two main ways to use contact metadata:

  1. Core API: Direct functions for creating, saving, loading, and searching contacts

    • create / create_from_xdg: Initialize a contact store
    • save / lookup / delete / list: CRUD operations
    • search_all: Flexible search across contact names
    • find_by_name / find_by_name_opt: Exact name matching
  2. Cmdliner Integration (Sortal.Cmd module): Ready-to-use CLI commands

    • list_cmd: List all contacts
    • show_cmd: Show detailed contact information
    • search_cmd: Search contacts by name
    • stats_cmd: Show database statistics
    • Pre-configured Cmd.info and argument definitions for easy integration

CLI Tool#

The library includes a standalone sortal CLI tool:

# List all contacts
sortal list

# Show details for a specific contact
sortal show avsm

# Search for contacts
sortal search "Anil"

# Show database statistics
sortal stats

Project Status#

Fully implemented and tested with 409 imported contacts.