{"id":15774635,"url":"https://github.com/passcod/figleaf","last_synced_at":"2025-03-31T14:23:52.318Z","repository":{"id":139959799,"uuid":"220425190","full_name":"passcod/figleaf","owner":"passcod","description":"Configuration as flexible as a leaf","archived":false,"fork":false,"pushed_at":"2020-11-04T21:12:46.000Z","size":218,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-10-05T16:22:32.303Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/passcod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-08T08:49:33.000Z","updated_at":"2020-12-30T15:47:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"8cf80b89-8050-4a7f-a415-4bf3d113694f","html_url":"https://github.com/passcod/figleaf","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/passcod%2Ffigleaf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/passcod%2Ffigleaf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/passcod%2Ffigleaf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/passcod%2Ffigleaf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/passcod","download_url":"https://codeload.github.com/passcod/figleaf/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246481127,"owners_count":20784481,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-04T16:22:34.135Z","updated_at":"2025-03-31T14:23:52.298Z","avatar_url":"https://github.com/passcod.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Figleaf\n\n_“Crackfic-taken-seriously in the form of an open-source project readme,” the configuration library._\n\n- **Not published on crates.io**\n- MSRV: latest stable, no version bump\n- License: [CC-BY-NC-SA](./LICENSE)\n- Contributions: use [the Caretaker model](https://caretaker.passcod.name)\n\n\u003csup\u003eIn case this is unclear: this is not and will never be actual working software.\u003c/sup\u003e\n\n## Quick-start\n\n```toml\nfigleaf = { git = \"https://github.com/passcod/figleaf\", version = \"0.1\" }\n```\n\n```rust\nuse figleaf::prelude::*;\n\n#[derive(Clone, Debug, Deserialize)]\nstruct Settings {\n  username: String,\n  password: String,\n  friends: Vec\u003cString\u003e,\n\n  #[serde(default)]\n  lollipop: bool,\n\n  lunch: Lunch,\n}\n\n#[derive(Clone, Debug, Deserialize)]\nstruct Lunch {\n  #[serde(rename = \"type\")]\n  kind: String,\n  quantity: u8,\n}\n\nlet settings: Settings = figleaf::auto().await?;\ndbg!(settings);\n```\n\nThis will read from:\n\n- Environment: `CRATENAME_USERNAME`, etc.\n- Config files named `cratename.ext`, where `ext` is any of the\n  [default supported languages](#the-default-set), in a hierarchy\n  of overrides:\n  + Local: working directory\n  + Runtime: `/run` and equivalents\n  + User: `XDG_CONFIG_PATH/CRATENAME/` and equivalents\n  + System: `/etc/CRATENAME/` and equivalents\n  + Vendor: `/usr/lib/CRATENAME/` and equivalents\n\nOverrides are top-level only and additive for collections. That is:\n\n```toml\n# ./cratename.toml\n\nusername = \"yourie\"\npassword = \"3194EA270314DF0A8D9B2BD\"\nfriends = [\"jaimie\", \"nickki\"]\nlunch = { type = \"sandwich\", quantity = 1 }\n\n\n# /etc/cratename/cratename.toml\n\nusername = \"brenden\"\nlunch = { type = \"sushi\", quantity = 4 }\nfriends = [\n  \"gostabo\",\n  \"valrie\",\n  \"joss\"\n]\n```\n\nwill load as:\n\n```rust\nSettings {\n    username: \"yourie\",\n    password: \"3194EA270314DF0A8D9B2BD\",\n    friends: [\n        \"gostabo\",\n        \"valrie\",\n        \"joss\",\n        \"jaimie\",\n        \"nickki\",\n    ],\n    lollipop: false,\n    lunch: Lunch {\n        kind: \"sandwich\",\n        quantity: 1,\n    },\n}\n```\n\n## Table of contents\n\nUnlinked items are not (or only partially) written yet.\n\n- [Concepts]\n- [Definition](#definition-of-configuration-structure)\n  + [Arbitrary](#arbitrary-value)\n  + [Serde deserialisable](#serde-deserialisable-only)\n    * [Refresher](#refresher-on-serde-syntax)\n    * [Helpers](#serde-helpers)\n  + [Macro](#figleaf-macro)\n    * [Standalone](#standalone-use)\n    * [Alongside serde](#alongside-serde)\n    * [Derived fields](#derived-fields)\n    * [Switched fields](#switched-fields)\n    * [Documentation comments](#documentation-comments)\n    * [Contextual documentation](#contextual-documentation)\n    * [Conditionals](#runtime-field-conditions)\n- [Generators](#generators)\n  + [Readme](#readme)\n  + [Example config file](#example-configuration-file)\n  + [Man page](#man-page)\n  + [JSON](#json)\n- [Loading](#loading)\n  + [Async](#async)\n  + [Blocking](#blocking)\n  + [Sync](#all-synchronous)\n  + [Singleton](#singleton)\n    * [Eager](#static-config-loaded-eagerly)\n    * [Lazy](#static-config-loaded-lazily)\n    * [With main](#dynamic-config-in-main)\n- [Recursive reconfiguration](#recursive-reconfiguration)\n- [In libraries](#in-libraries)\n  + [Usage]\n  + [Discovery]\n  + [Overrides]\n  + [Excludes]\n  + [Remapping]\n- [Languages](#languages)\n  + [Defaults](#the-default-set)\n  + [Compile-time selection](#compile-time-selection)\n  + [Additional](#using-additional-languages)\n  + [Runtime selection](#runtime-selection)\n  + [File extentions and mimetypes](#file-exensions-and-mimetypes)\n  + [Auto-detection](#auto-detection)\n- [Sources](#sources)\n  + [Compile-time selection](#compiling-in)\n  + [Runtime filtering](#filtering)\n- [Environment](#environment)\n  + [Key format (prefix, etc)](#key-format)\n  + [Value parsing]\n  + [Blobs]\n- [Arguments]\n  + [Wildcards / globs]\n  + [Positional]\n  + [Pico]\n  + [Clap]\n    * [From Figleaf]\n    * [From existing]\n- [Files]\n  + [Lookup paths]\n  + [Hierarchy]\n  + [Permissions and file attributes]\n  + [Overriding]\n  + [Includes]\n    * [Via `.d` folders]\n    * [Via language]\n    * [Via recursion]\n    * [Via preprocessor]\n  + [With a custom reader]\n  * [Memory-mapped]\n- [Network]\n  + [HTTP]\n    * [HTTPS]\n    * [HTTP/3]\n  + [TCP]\n    * [TLS]\n    * [Domain sockets]\n  + [UDP]\n    * [Passive]\n    * [Active]\n    * [QUIC]\n    * [Datagram sockets]\n  + [DNS]\n    * [mDNS]\n- [Databases]\n  + [Connection]\n    * [Configuration]\n    * [Reuse]\n    * [Pooling]\n  + [Relational]\n    * [SQLite]\n    * [MySQL]\n    * [Postgres]\n    * [MSSQL]\n    * [Cassandra]\n    * [Redshift]\n  + [Key-value]\n    * [Redis-like]\n    * [LevelDB-like]\n    * [Sled]\n    * [Etcd]\n    * [Consul]\n    * [Riak]\n  + [Document]\n    * [Mongo]\n    * [Couch]\n- [Platorm-specific]\n  + [D-Bus]\n  + [Windows COM]\n  + [Windows Registry]\n  + [Apple Events]\n  + [Virtual filesystems]\n- [Special]\n  + [Standard input]\n  + [Appended to binary]\n  + [Shared memory]\n  + [Keyring]\n  + [Clipboard]\n  + [EFI variables]\n  + [Hardware tokens]\n  + [Serial]\n  + [Block device]\n- [Reloading]\n  + [Watching and polling]\n  + [Signals]\n  + [Reconfiguration]\n  + [Environment]\n  + [Files]\n  + [Network]\n  + [Database]\n- [Encryption]\n  + [Symmetric]\n  + [Public key]\n    * [Signing]\n  + [Layered]\n  + [Partial]\n    * [By method]\n    * [By file]\n    * [By key]\n  + [From hardware module]\n  + [Secrets in memory]\n- [Appendices](#appendices)\n  + [libfigleaf](#libfigleaf)\n    * [Obtaining](#obtaining-libfigleaf)\n    * [Customising](#custom-build)\n    * [Configuring](#dynlib-loading-options)\n    * [Disabling](#disabling-dynamic-loading)\n  + [Feature profiles]\n  + [Feature reference]\n  + [Optimisation guidelines]\n  + [Bindings]\n    * [C/C++]\n    * [WASM] /* with optional inbound bindings to provide *s etc access */\n    * [Node.js]\n    * [Deno] /* 1) via wasm, with deno-sided fs/env/args *inks; 2) as native op crate once that lands in deno */\n    * [Ruby]\n    * [PHP]\n    * [Swift]\n\n## Definition of configuration structure\n\nA lot of the magic of Figleaf comes from defining and describing the\ndatastructure that is the result of loading configuration. Generally, that\ndatastructure is a **field struct**, though it can also be an **enum** or\n**tuple struct** or another kind of type.\n\nThere are three main categories of structure:\n\n### Arbitrary value\n\nWhile not recommended, as that makes a lot of Figleaf unavailable, it is\nentirely possible to return a value that can be traversed dynamically instead\nof parsing into a custom structure. This is made possible by the\n[`arbitrary::Value`] type.\n\nTODO: example\n\n### Serde deserialisable only\n\nWhile most of the power of Figleaf comes with the macro introduced in the next\nsection, a structure that simply derives Serde's [`Deserialize`] trait is\nalready and immediately useful, as Figleaf will parse configuration inputs into\nthat structure, and Serde's attributes can be used to add a lot of flexibility\nto the structure itself.\n\n#### Refresher on Serde syntax\n\n(Always refer to [the Serde documentation] for details and exhaustive options.)\n\nTODO: the derive, rename and rename all, the enum styles, skip, with_.\n\n#### Serde helpers\n\nFigleaf also provides its own helpers that augment the Serde deserialisation,\nespecially for the `with_*` attributes.\n\nTODO: list helpers\n\nThe `arbitrary::Value` mentioned previously can also be used as a field\nanywhere in a Serde structure, for partial fully-dynamic configuration values.\n\nTODO: example\n\n### `#[figleaf]` macro\n\nThis macro, applied on a configuration datastructure alone or alongside Serde,\nenables the full power of Figleaf, through attributes on the structure, its\nsubstructures, and fields.\n\nThe full list of options can be found [in the API documentation]. What follows\nis an overview and reference of the basics and the most useful features.\n\n#### Standalone use\n\nIt's possible to apply only the macro to a structure, without Serde. In this\ncase, the `parse=` top-level attribute is required. Its function takes an\n[`arbitrary::Value`] and must return an [`error::Result\u003cT\u003e`] where `T` is the\nstructure the attribute is applied to.\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf(parse = Config::parse)]\nstruct Config {\n    key: String,\n}\n\nimpl Config {\n    fn parse(raw: \u0026arbitrary::Value) -\u003e figleaf::error::Result\u003cSelf\u003e {\n        let key = raw.get(\"key\")?;\n        Ok(Self { key })\n    }\n}\n```\n\n#### Alongside Serde\n\nWhen the structure implements `Deserialize`, the `parse=` top-level attribute\nisn't allowed (as that is implemented by Serde).\n\nThe macro implements various facilities described in this and later sections.\nIt is immediately useful: further customisation via attributes is described\nbelow but the core benefits are already available with default settings as soon\nas the macro is added.\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf]\n#[derive(Debug, Deserialize, Clone)]\nstruct Config {\n    key: String,\n}\n\n// Load directly from struct:\nlet config = Config::load().await?;\n\n// All figleaf::Loadable methods are available:\nconfig.reload().await?;\n\n// Generators are enabled:\ndbg!(Config::generate_markdown());\n```\n\nTODO: Serde-specific integrations\n\n#### Derived fields\n\nMarking a field with `#[figleaf(derive_with = function)]` creates a\nonce-computed field: the value is computed once when the configuration is\nloaded by calling the function provided with a reference to the\npartially-constructed structure.\n\nIf the field implements `Default`, that is used for the initial value while\nconstructing the structure. Otherwise, the structure is partially constructed\nusing `MaybeUninit`. In any case, accessing the field being derived on the\npassed-in partially-constructed structure is strongly discouraged and may be\nundefined behaviour.\n\n```rust\n#[figleaf]\n#[derive(Deserialize)]\nstruct Config {\n    loaded: String,\n\n    #[figleaf(derived_with = derivation)]\n    derived: String,\n}\n\nfn derivation(partial: \u0026Config) -\u003e String {\n  partial.loaded.trim().into()\n}\n```\n\n#### Switched fields\n\n**In most cases, prefer derived fields.** However, in some cases, having the\nloaded value hanging around is undesirable, or you may need to consume the\nloaded value to create the derived one and don't want to add an `Option` in.\n\nWhatever the reason, switched fields have one type when loading, and another\ntype once constructed. They can also be the same type in both cases, but with\nthe loaded value being passed as owned to the switching function.\n\n```rust\n#[figleaf]\n#[derive(Deserialize)]\nstruct Config {\n    #[figleaf(switch_from = usize, switch_with = switcher)]\n    switched: String,\n}\n\nfn switcher(value: usize, _partial: \u0026Config) -\u003e String {\n  format!(\"{}\", value * 2)\n}\n```\n\nWhile merely discouraged in the case of derived fields, **accessing a switched\nfield from the `partial` reference is strictly forbidden** and _is always\nundefined behaviour_.\n\nA switched field cannot also be a derived field and vice versa.\n\nThe `switch_with` function defaults to calling `.into()` if not provided.\n\n#### Documentation comments\n\nDocumentation comments on fields and the structure itself are used to render\ndescription and help text in generated representations:\n\n- error messages\n- readme generator\n- man page generator\n- example config file generator\n\nTo disable using this documentation, use the `#[figleaf(no_doc)]` attribute. To\ndisable this behaviour for all fields, add `#[figleaf(opt_in_docs)]` to the\ntop-level. When that is set, `#[figleaf(use_doc)]` on fields can be used to opt\nin on a field-by-field basis.\n\n#### Contextual documentation\n\nSome documentation may be superfluous in some formats or only make sense in\nothers. It is possible to include or exclude all or part of any documentation\ncomment based on the generation target, in two different forms: inline to doc\ncomments, or as attributes.\n\n```rust\n/// The first documentation paragraph/line.\n///\n/// Common information.\n///\n/// # [figleaf(target = \"man\")]\n///\n/// This section is only shown on the man page.\n///\n/// # [figleaf(not(target = \"rustdoc\"))]\n///\n/// This section is not shown in the rustdoc output.\nfield: Item,\n```\n\n```rust\n#[figleaf(target_doc(\"man\", \"This string replaces the documentation entirely for man pages\"))]\n#[figleaf(target_doc(\"commonmark\", \"This replaces the readme description\")]\nfield: Item,\n```\n\nThese doc targets are available by default:\n\n- `rustdoc`: default/rustdoc output\n- `man`: man pages\n- `example`: example config file\n- `commonmark`: readme/commonmark/markdown documentation\n\n#### Runtime field conditions\n\nUsing conditional attributes, fields can be skipped or included based on\n_runtime_ conditions. For compile-time conditionals, use Rust's own `cfg`\nattributes.\n\n```rust\n#[figleaf]\n#[derive(Deserialize)]\nstruct Config {\n    #[figleaf(skip_on(source = \"lang:toml\"))]\n    skipped_for_toml: bool,\n\n    #[figleaf(only_on(source = \"lang:dhall\"))]\n    only_when_loading_from_dhall: bool,\n\n    #[figleaf(skip_on(custom = \"skipper\"))]\n    custom_runtime_skip: bool,\n\n    #[figleaf(only_on(custom = \"includer\"))]\n    custom_runtime_include: bool,\n}\n\nfn skipper(_partial: \u0026Config) -\u003e bool {\n    /* skip = */ true\n}\n\nfn includer(_partial: \u0026Config) -\u003e bool {\n    /* include = */ true\n}\n```\n\nAvailable conditional attributes:\n\n- `lang:*`: configuration language used to load this structure\n- `custom`: custom function\n- `reload`: `\"true\"` when the config is being reloaded\n- `singleton`: `\"true\"` when loading as a singleton\n- `source`: `file`, `env`, `args`, `db:*`...\n\n## Generators\n\nFigleaf comes with several generators that create useful output from your\nconfiguration definition. Due to technical limitations, these need to be called\nfrom your own code and return strings — you can then output to screen or to\nfile as you see fit.\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf]\n#[derive(Deserialize)]\nstruct Config {\n    field: String,\n}\n\nprintln!(\"{}\", Config::generate(generators::Output::JSON));\n```\n\n### Readme\n\nYou can output a readme fragment describing your configuration file in various\nreadme formats:\n\n- `plain`\n- `commonmark` (aka Markdown)\n- `asciidoc`\n\nTODO: output example\n\n### Example configuration file\n\nYou can output a configuration file with all fields commented out and with\ncomments containing the documentation for each field. Supported languages:\n\n- `toml`\n- `dhall`\n- `yaml`\n\nTODO: output example\n\nBecause JSON does not support comments, it is not supported.\n\n### Man page\n\nYou can output a man page (roff). This will be named `CRATE_NAME(5)` (Section 5\nis for file formats and configuration files.) You can opt to specify a\nconfiguration language to use for the man page, or it will be written in a\nlanguage-independent style.\n\nTODO: output example\n\n### JSON\n\nThe JSON output format describes everything that Figleaf knows about your\nconfiguration definition as well as its own configuration (version, which\nfeatures were enabled, etc). This format is regulated by semver so it can be\nrelied on.\n\nSee the [`generators::json`] module documentation for the exhaustive reference.\n\n## Loading\n\nFigleaf supports different APIs for loading the configuration. The main\ndistinction is around asynchronicity. There is also a singleton style with\nseveral variants.\n\n### Async\n\nThe default loading style uses async:\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf]\n#[derive(Deserialize)]\nstruct Config { etc: bool }\n\nlet config = Config::auto().await?;\n```\n\nThe [`auto`] function selects all defaults and finalises the builder, which\nresolves into a Future which is then `await`ed into a Result.\n\nConstructing a builder manually:\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf]\n#[derive(Deserialize)]\nstruct Config { etc: bool }\n\nlet builder = Config::build()\n  .basename(\"parka\")\n  .languages(\u0026[Language::TOML, Language::JSON])\n  .finalise()\n  .await?;\n```\n\n### Blocking\n\nIf dealing with awaiting futures is too much trouble, there is a blocking API\nwhich embeds an async executor. To reduce depedencies in the default case, this\nis behind a feature.\n\n```toml\n[dependencies.figleaf]\nversion = \"...\"\nfeatures = [\"load:blocking\"]\n```\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf]\n#[derive(Deserialize)]\nstruct Config { etc: bool }\n\nlet builder = Config::build()\n  .basename(\"parka\")\n  .languages(\u0026[Language::TOML, Language::JSON])\n  .finalise_blocking()?;\n```\n\nNote the different finalise function.\n\n### All-synchronous\n\nEmbedding an executor adds a lot of dependencies, and using async IO does too,\nand may be complicated in some environments. Whatever the reason, there is also\nan all-synchronous API, which uses synchronous IO from the stdlib instead of\nasync IO:\n\n```toml\n[dependencies.figleaf]\nversion = \"...\"\nfeatures = [\"load:sync\"]\n```\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf]\n#[derive(Deserialize)]\nstruct Config { etc: bool }\n\nlet builder = Config::build()\n  .basename(\"parka\")\n  .languages(\u0026[Language::TOML, Language::JSON])\n  .finalise_sync()?;\n```\n\nIt is recommended to disable default features and only using this loading\nfeature in this case, to cut down dependencies and code size / compile time.\n\n### Singleton\n\nA common pattern is to load config at program start, put it in a static, and\nhave it be available to all parts of your program without passing a variable\naround. Figleaf's singleton support streamlines this, and comes in three\nvariants.\n\nIn all cases, the default `auto()` builder is used unless a builder function is\nprovided:\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf(singleton = \"eager\", singleton_builder = builder)]\n#[derive(Deserialize)]\nstruct Config { etc: bool }\n\nfn builder() -\u003e figleaf::Builder\u003cConfig\u003e {\n  Config::build()\n    .basename(\"polo\")\n}\n```\n\n#### Static config, loaded eargerly\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf(singleton = \"eager\")]\n#[derive(Deserialize)]\nstruct Config { etc: bool }\n\ndbg!(Config::singleton().etc);\n```\n\nThis variant loads at program start, without needing a hook in `main`. Errors\nencountered while loading panic.\n\n#### Static config, loaded lazily\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf(singleton = \"lazy\")]\n#[derive(Deserialize)]\nstruct Config { etc: bool }\n\ndbg!(Config::singleton().etc);\n```\n\nThis variant is like the eager one, but does nothing at program start, and is\ninstead loaded on the first access.\n\n#### Dynamic config, in main\n\nIf more context is needed to create the builder, or error handling is required,\nthe singleton can be created from your own code (i.e. in main):\n\n```rust\nuse figleaf::prelude::*;\n\n#[figleaf(singleton = \"lazy\")]\n#[derive(Deserialize)]\nstruct Config { etc: bool }\n\nfn main() -\u003e Result\u003cBox\u003cdyn std::error::Error\u003e\u003e {\n  let args = get_some_args()?;\n  Config::build().basename(args.config_name).as_singleton_sync()?;\n\n  dbg!(Config::singleton().etc);\n}\n\ndbg!(Config::singleton().etc);\n```\n\nThe `as_singleton` method supports the same loading features as the\n`finalise()` method (here shown with `load:sync`).\n\n## Recursive reconfiguration\n\nRecursive reconfiguration is one of our flagship advanced features. To\nunderstand what it does, consider the simple case of an environment variable\nthat dictates what the configuration files are named: in a traditional system,\nthis would either need two separate systems (loading and parsing the\nenvironment, and loading the configuration), loading the configuration twice\nbased on a condition, or some other manual process.\n\nHere, instead, as long as you tell Figleaf about it, it will take care of\nrecursively loading configuration, checking reconfiguration fields, and\nadjusting until done.\n\nThis makes possible complex scenarios, such as (all subject to configuration,\nnone of these are Figleaf default strategies):\n\n- changing the prefix of environment variables from an environment variable;\n- specifying a `DATABASE_URL` to enable loading the rest of the config from a\n  database table;\n- a `zeroconfig_ns=` key in a TOML file points to a Zeroconfig namespace on the\n  local network where a configuration server listening on a TCP port advertises\n  its address;\n- the PCI address of a hardware token is provided via a blob concatenated to\n  the binary, which is used to decrypt the rest of the concat'd blob into\n  configuration secrets; meanwhile the innoffensive part of the config is\n  loaded from plain text file as per normal.\n\nReconfigurability does not require fields to be wrapped in `Option`s, instead\nusing `MaybeUninit` to partially construct the structure as it is loaded, in\nthe same way that derived and switched fields are filled in. Figleaf ensures\nthat you never encounter partially-initialised or uninitialised data once the\nconfiguration is entirely loaded, but (as mentioned there) derived and switched\nfields may deal with such uninitialised data.\n\nReconfiguration is primarily configured via the `#[figleaf(depends_on = ...)]`\nattribute on fields, which specifies one or more fields the field the attribute\nprecedes depends on, and the `::using()` builder method which specifies\nadditional sources (tried in inserting order) the configuration should be\nobtained from.\n\nTODO: usage\n\n## In libraries\n\nFigleaf can be used in libraries, not just top-level applications.\n\nIf a library needs configuration, in addition to offering a configuration\nstruct to its consumers to be completed at their discretion, it can request\nconfiguration via Figleaf, with the same power features as usual Figleaf usage.\n\nThe difference is that the top-level application always can have control if it\nso decides. It can discover configuration requests, and intercept, redirect,\nremap, override... them. If the top-level application either doesn't use\nFigleaf or doesn't interfere with library configuration requests, things\nproceed as usual.\n\n### TODO: the rest\n\n## Languages\n\nConfiguration languages are used to parse files and file-like inputs. Figleaf\nworks exclusively with Serde-implemented languages (Serde calls them\n“formats”). There are several ways to select which languages are available, and\nwhich is used.\n\n### The default set\n\n- [JSON](https://en.wikipedia.org/wiki/JSON). Feature: `lang:json`.\n- [TOML](https://en.wikipedia.org/wiki/TOML). Feature: `lang:toml`.\n- [YAML](https://en.wikipedia.org/wiki/YAML). Feature: `lang:yaml`.\n- [Dhall](https://dhall-lang.org). Feature: `lang:dhall`.\n\nJSON, TOML, and YAML are the most used configuration languages.\n\nDhall is the author’s hope for a better configuration standard.\n\n### Compile-time selection\n\nAll built-in languages are feature-gated (with the default set being included\nin the crate’s default features), all denoted by `lang:` as demonstrated above.\n\nThe non-default built-in languages are:\n\n- [Dhall](https://dhall-lang.org). Feature: `lang:dhall`.\n- [HCL](https://github.com/hashicorp/hcl). Feature: `lang:hcl`.\n- [Human JSON](https://hjson.org). Feature: `lang:hjson`.\n- [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md). Feature: `lang:hocon`.\n- [INI](https://en.wikipedia.org/wiki/INI_file). Feature: `lang:ini`.\n- [JSON5](https://json5.org). Feature: `lang:json5`.\n- [MuON](https://github.com/muon-data/muon). Feature: `lang:muon`.\n- [PHP `serialize`](https://www.php.net/manual/en/function.serialize.php). Feature: `lang:php`.\n- [RON](https://github.com/ron-rs/ron). Feature: `lang:ron`.\n- [S-expressions using `lexpr`](https://github.com/rotty/lexpr-rs). Feature: `lang:lexpr`.\n- [XML](https://en.wikipedia.org/wiki/XML). Feature: `lang:xml`.\n\nThe (not recommended apart from prototyping and testing) `lang:all` feature\nincludes all supported built-in languages.\n\n### Using additional languages\n\nUsing more languages than the built-in ones is possible: all that's needed is a\nserde format library, a conversion from its `Value` type to Figleaf’s, and an\nextension/mime-type list.\n\nTODO: example\n\n### Runtime selection\n\nBy default, all languages compiled-in are used. However, selecting languages\ncan be done at runtime.\n\nTODO: example\n\n### File extensions and mimetypes\n\nEach language has one or more associated file extensions and one or more IANA\nmedia type (commonly known as “mimetype”). The built-in ones are:\n\n|    Language    | Extensions            | Media types |\n|:--------------:|:----------------------|:------------|\n| Dhall          | `dhall`               | (none) |\n| HCL            | `hcl`                 | (none) |\n| Human JSON     | `hjson`               | `application/hjson` |\n| HOCON          | `hocon`, `hoconf` [1] | `application/hocon` |\n| INI            | `ini`                 | `application/textedit`, `zz-application/zz-winassoc-ini` |\n| JSON           | `json`                | `application/json` |\n| JSON5          | `json5`               | `application/json5` |\n| MuON           | `muon`                | (none) |\n| PHP’s serialize| (none)                | (none) |\n| RON            | `ron`                 | (none) |\n| S-expressions  | (none)                | (none) |\n| TOML           | `toml`                | `application/toml` |\n| XML            | `xml`                 | `application/xml` |\n| YAML           | `yaml`, `yml`         | `application/yaml`, `application/x-yaml`, `text/yaml` |\n\n\u003csup\u003e\n[1] HOCON’s default is `.conf`, but that is supremely ambiguous.\nYou can still opt-in to this usage with `.add_extension(Language::Hocon, \"conf\")`.\n\u003c/sup\u003e\n\nExtensions are used when reading files, media types when reading streams with\nan indicated content type or when detecting content type e.g. with a “magic”\ndatabase. It’s always possible to add or override the extensions and media\ntypes for a language:\n\n```rust\nbuilder\n  .add_extension(Language::YAML, \"yuml\")\n  .set_mediatypes(Language::YAML, \u0026[\"application/yuml\"])\n```\n\n### Auto-detection\n\nFor streams, files, or file-like sources without a content type indication or\nextension, type can be auto-detected. There are three ways to achieve this:\n\n1. Trying all enabled formats in turn until one doesn’t error. This is the\n   default, as it is easiest, even though it is innefficient and may result in\n   false-positives and errors.\n\n2. Using a “magic” database. Enabled with the `magic-detect` feature. The\n   general principle is that the first few hundred bytes of a file are read and\n   a series of heuristics are applied. This returns a media type, which is used\n   as defined above. In case nothing is found, falls back to 1.\n\n3. Providing a function that takes the source information and returns a\n   `Language::` enum variant or `None`. Falls back to 1 (or 2 if enabled).\n\nTODO: builder example code for 2 and 3\n\n## Sources\n\n- [Environment](#environment)\n- [Arguments](#arguments)\n- [Files]\n- [Network]\n- [Databases]\n  + [Connection]\n  + [Relational]\n  + [Key-value]\n  + [Document]\n- [Platorm-specific]\n  + [D-Bus]\n  + [Windows COM]\n  + [Windows Registry]\n  + [Apple Events]\n  + [Virtual filesystems]\n- [Special]\n  + [Standard input]\n  + [Appended to binary]\n  + [Shared memory]\n  + [Keyring]\n  + [Clipboard]\n  + [EFI variables]\n  + [Hardware tokens]\n  + [Serial]\n  + [Block device]\n\nMost of the sections below deal with the various kinds of configuration sources\nthat Figleaf supports, either out of the box or as features. Sources are where\nconfiguration lives and is pulled from into your program.\n\nEach different source described starts out with a table like this:\n\n| Feature        | `source:\u003cname\u003e` ||\n|:---------------|:----------:|:--------------------------------------------------------------|\n| Default        | yes/**no** | Is it in the default feature set?                             |\n| Reloadable     | yes/**no** | Is it [reloadable]?                                           |\n| Watchable      | yes/**no** | If it is reloadable, can changes be detected without polling? |\n| Writable       | yes/**no** | Does it support [writing back to the source]?                 |\n| Can be dynamic | yes/**no** | Can it be built into a shared library? (see below)            |\n| In libfigleaf  | yes/**no** | Is it in the default shared library build?                    |\n| WASM           | yes/**no** | Can it be [used in Web Assembly]? (potentially needing binds) |\n\n### Compiling-in\n\nEach source can be compiled-in (to compiled-out default features, use\n`default-features = true`) with crate feature names namespaced under `source:`.\nTo see the full list of source features, check out [the appendix].\n\nAlso see the [feature profiles] appendix for presets.\n\n### Filtering\n\nBy default, if a source is compiled in (if its feature is enabled), it is used.\nSource filtering takes a [Pattern] and matches on source names: positive matches\nare allowed. For security-sensitive applications, source safelisting may be\nimportant, though disabling sources at compilation time is likely better.\n\nSource filtering can also be used in recursive reconfiguration scenarios, with\na configuration option enabling more configuration sources.\n\nTODO: example\n\n## Environment\n\n| Feature | Default | Reloadable | Watchable | Writable | Dynamic | In libfigleaf | WASM |\n|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|\n| `source:env` | yes | yes | **no** | yes | yes | yes | yes |\n\nEnvironment variables are key-value pairs provided by the operating system to\na process and its children. Keys and values are arbitrary byte strings, with the\nnotable exception of the null byte (0x00) being disallowed in both. In practice,\nnon-ascii keys are discouraged, and `UPPER_SNAKE_CASE` is conventional.\n\n(Technically, environment variables are a set of null-terminated byte strings,\nthey're only key-value pairs in that this is how everything interprets them.\nKey and value are delimited by the first `=`, if present. Having no value or\nthe empty string as a value is equivalent, and is generally interpreted as\nbeing unset, as there is no true way to unset a particular environment\nvariable. Similarly, the order in which environment variables are provided is\nimplementation-specific and may not be defined: they are usually to be\nconsidered an unordered set; yet internally they may be considered an ordered\narray.)\n\nEnvironment variables may be written to by the process itself. Child processes\nand other processes (including root) cannot change a process's environment once\nthat process has been created. Nevertheless, environment may be written to by\nother parts of the program, and thus this source does have reloading support.\n\n\u003csup\u003eSecurity note: by default environment variables are available to child\nprocesses (including when dropping privileges) and any other process running\nunder the same user (or root). While there are various mitigations, keep this in\nmind when passing in secrets or sensitive information.\u003c/sup\u003e\n\n### Key format\n\nThe general convention for environment variables as configuration is to prefix\nkeys with the name or a short identifier of the application. For example a\nprogram named Passionfruit may use the `PASSIONFRUIT_` or perhaps the `PASSION_`\nprefix.\n\n```rust\nbuilder\n  .configure(env::Prefix(\"PASSIONFRUIT_\"))\n```\n\nWhen using the `auto!` macro, the prefix is set to the `UPPER_SNAKE_CASE`\nvariant of the crate's name, appended with an underscore. Otherwise it defaults\nto no prefix (the empty string).\n\nKeys can be further transformed using an arbitrary function. By default, keys\nare treated as UTF-8 and lowercased, with non-UTF-8 bytes left alone. That\ntransform can be disabled to leave keys as-is.\n\n```rust\nbuilder\n  .configure(env::KeyTransform(None))\n  .configure(env::KeyTransform(Some(|key: \u0026[u8]| {\n    use bstr::ByteSlice; // provides .replace\n    key.replace(\"leaves\", \"shoots\")\n  })))\n```\n\nTransforming keys may result in collisions, which can be handled in various ways:\n\n- `env::Collision::LastWins` (the default), where every subsequent identical key\n  overrides the previous value, resulting in the \"last\" key \"winning\" overall.\n- `env::Collision::FirstWins`, where subsequent identical keys are ignored.\n- `env::Collision::Error`, where duplicate keys makes Figleaf return an error.\n- `env::Collision::Joined(\":\")`, where duplicate keys have their values joined\n  together with the given separator (e.g. `:`).\n\n```rust\nbuilder\n  .configure(env::Collision::Error)\n```\n\n### Value parsing\n\nValue parsing can be customised globally and also per key pattern. By default,\nthe following rules are applied:\n\n1. If a value is entirely digits, then the smallest unsigned integer type the\n   value fits in is selected. If the value doesn't fit in the largest integer\n   type available (u128 on most platforms), then if `source:args:bigint` is\n   enabled the value is parsed as a [`BigUint`], and otherwise it falls through\n   (to a string).\n\n2. If a value is entirely digits save for a starting minus sign (U+TODO) the\n   same process as in 1 is followed, but for signed integers (and [`BigInt`] if\n   enabled). Similarly for floats in decimal notation, and in exponent notation.\n\n3. If the value is literally `true` or `false`, case-insensitive, it is parsed\n   to the appropriate boolean.\n\n4. If the value is valid UTF-8, it is parsed as a `String`.\n\n5. Otherwise, byte strings are returned raw.\n\n## Arguments\n\n| Feature | Default | Reloadable | Watchable | Writable | Dynamic | In libfigleaf | WASM |\n|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|\n| `source:args` | yes | **no** | **no** | **no** | **no** | yes | yes |\n\nArguments are provided to a program as an ordered array of arbitrary byte\nstrings, excluding null bytes. Encoding may be OS- or implementation- specific.\nThey are generally considered to be immutable; however it can be possible to\noverwrite them directly in memory. Figleaf explicitly does not support this and\nconsiders this source read-only.\n\nThere are many different conventions for parsing arguments in key-value pairs\nor more complex structures. Figleaf by default attempts to simplistically parse\nthe most common styles all at once:\n\n - `-arg VALUE`, `-arg=VALUE`, `/arg VALUE`, `/arg=VALUE`, `--arg VALUE`,\n   `--arg=VALUE` all result in the key `/arg`. It makes no difference between\n   \"long\" and \"short\" options: `-a` and `--a` and `/a` are parsed the same way\n   and are equivalent. The `/` variants are only enabled on Windows to avoid\n   clashing with paths on Unices.\n\n - If an option as per above is immediately followed by another option or the\n   end of the argument list or `--`, it is treated as a unary option (often\n   called \"flag\").\n\n - All arguments following `--` are provided as-is, in order, in a Vec.\n\nTODO: clap integration\n\nValues are parsed in the same way as environment variables' are.\n\n## Appendices\n\n- [libfigleaf](#libfigleaf)\n- [Feature profiles]\n- [Feature reference]\n- [Optimisation guidelines]\n- [Bindings]\n\n### libfigleaf\n\nFigleaf is enormous. It supports lots of languages, lots of sources, and lots of\ndifferent options. Everything has a corresponding crate feature, so most of the\nlibrary can be opt-in, or opt-out in the case of the default feature set. Yet\nevery feature enabled has two costs: compilation time and binary size.\n\nEnabling all the languages and all the sources makes for a powerful and truly\nflexible configuration library, but a terrible developer experience. Enabling a\nsubset in development and more for release builds is awkward and may introduce\nbugs in production not present or noticed in development! It's not great.\n\n**libfigleaf** is a shared library containing by default all built-in languages\nand nearly all built-in sources. Installed in a standard system location, it\nwill be loaded by Figleaf and all available sources and languages enabled. Thus\nthe main library can remain both powerful and pleasant to use.\n\nAnother advantage is that libfigleaf may be packaged separately, for example\nthrough a package manager, and shared between many application, further reducing\nsize, as well as enabling updates to libfigleaf independently of your own app.\n\nThe [`default:minimal` feature profile] heavily relies on libfigleaf: along with\nother optimisations, it enables only the environment variable source as built-in,\nno languages, and delegates all else to the shared library.\n\n#### Obtaining libfigleaf\n\nThe Figleaf project provides a pre-built distribution of libfigleaf for:\n\n- Always: x86_64 and i686 Linux (libc and musl), Windows (MSVC), x86_64 macOS\n- Usually: x86_64 FreeBSD, Android, ARM64 iOS, Linux (libc), WASM32 (see [Bindings])\n- Best effort: x86_64 NetBSD, OpenBSD, Fuchsia, Redox, ARM64 Linux (musl)\n\nThe distribution is not built for every Figleaf release, and is versioned\nseparately. Current and previous releases, as well as packager instructions,\ncan be found at https://passcod.name/figleaf/libfigleaf/. All downloads are\nsigned using [minisign](https://jedisct1.github.io/minisign/). Public key:\n\n```\n1234567890qdrwbjfupashtgyneoizxmcvklQDRWBJFUPASHTGYNEOZX\n```\n\nTo obtain libfigleaf on another platform, you need to build it. You can obtain\nsource from either this repository or (preferably) a tarball of the source used\nto build the latest (or desired) libfigleaf release, also available and signed,\nas above. Building instructions are included. To build from here:\n\n```bash\n$ cd libfig\n$ cargo build --release\n$ cp target/release/libfig.so libfigleaf.so\n$ strip libfigleaf.so\n```\n\nAdjust for the correct filename/target/tooling.\n\n#### Custom build\n\nIt is possible to build your own custom distribution of the library (do **not**\ncall it \"libfigleaf\", that name must always refer to the default distribution).\n\nThe libfig member crate supports Figleaf's `lang:` and `source:` feature sets.\nRefer to its `Cargo.toml` for more. Change the `default` feature key to include\nwhat you need, or specify them on the command line.\n\nYou can also include your own custom sources and languages. Hook those up in\n`lib.rs` (search for `// custom language` and `// custom source`).\n\nBuild as above.\n\n#### Dynlib loading options\n\nBy default, Figleaf looks in system locations for the name `libfigleaf.V.EXT`\nwhere `V` is the major libfigleaf version compatible with this Figleaf release,\nand `EXT` is the platform-appropriate file extension (`so` or `dll`).\n\nIn debug mode (when `debug_assertions` and/or `dynamic:debug` is on), it also\nlooks in the working directory.\n\nYou can change the places it looks in (tried in order):\n\n```rust\nbuilder\n  .library_locations(\u0026[\"/opt/figleaf/lib\", \"./inc\"])\n```\n\nYou can change the name it looks for (tried in order):\n\n```rust\nbuilder\n  .library_names(\u0026[\"libmapleleaf\", \"figfruit\"])\n```\n\nYou can change when it stops:\n\n```rust\nbuilder\n  .library_multiple(true)\n```\n\nThat one is false by default. When true, Figleaf will keep loading libraries\nit finds until it exhausts the locations × names list. That can be used to\nload e.g. the default distribution and a custom add-on library on top.\n\nThe first version of a source or language found is used, and those compiled in\ntake precedence overall. That is, it's not possible to override already-loaded\nimplementations.\n\n#### Disabling dynamic loading\n\nYou may want to disable for a variety of reasons, the most common being as a\nsecurity concern.\n\nYou can disable at runtime:\n\n```rust\nbuilder\n  .library_load(false)\n```\n\nOr you can remove the feature set namespaced under `dynamic:`.\n\nYou can also disable loading sources or languages only:\n\n```rust\nbuilder\n  .library_load_sources(false)\n  .library_load_languages(false)\n```\n\nOr via features: `dynamic:sources` and `dynamic:languages`.\n\nThere are [feature profiles] preset without the dynamic features.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpasscod%2Ffigleaf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpasscod%2Ffigleaf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpasscod%2Ffigleaf/lists"}