devset_core/lib.rs
1//! Versioned file bundles, applied to a directory and updated without losing local edits.
2//!
3//! A *profile* is a directory of files, on disk or in git; a *target* is a directory that
4//! applies profiles as *layers*. devset records what it wrote in the target's `.devset/`, so a
5//! later update tells a local edit from a change the profile made: the edit is kept, and merged
6//! with the change where the file's [`Policy`](profile::Policy) says so. A profile owns a whole
7//! file, or a part of one its [`Scope`](profile::Scope) names: the keys its payload defines, or a
8//! marked block.
9//!
10//! # Applying a profile
11//!
12//! Every operation is a prefix of one chain, and only [`commit`](commit()) writes:
13//!
14//! ```
15//! use std::fs;
16//!
17//! use devset_core::source::Source;
18//! use devset_core::{Cache, Mode, Refresh, Target, commit, plan, resolve, survey};
19//!
20//! let dir = camino_tempfile::tempdir()?;
21//! let root = dir.path();
22//! # fs::create_dir_all(root.join("base/files"))?;
23//! # fs::write(root.join("base/profile.toml"), "[profile]\nname = \"base\"\n\n[files.\".editorconfig\"]\n")?;
24//! # fs::write(root.join("base/files/.editorconfig"), "root = true\n")?;
25//! # fs::create_dir(root.join("repo"))?;
26//! let mut target = Target::open_or_new(&root.join("repo"))?;
27//! target.add_layer(Source::Dir("../base".into()))?;
28//!
29//! let resolved = resolve(&target, &Cache::at(root.join("cache")), Refresh::None)?; // read, compose
30//! let survey = survey(resolved, &target)?; // compare with the disk
31//! let plan = plan(survey, Mode::Apply, &target)?; // decide, merge
32//! commit(plan, &target)?; // write, state last
33//!
34//! let applied = fs::read_to_string(root.join("repo/.editorconfig"))?;
35//! assert_eq!(applied, "root = true\n", "the profile's file, in the target");
36//! # Ok::<(), Box<dyn std::error::Error>>(())
37//! ```
38//!
39//! # Updating
40//!
41//! [`Refresh::All`] moves every layer to what its ref names now; the rest of the chain is the
42//! same. A `merge` file then merges its local edits with the profile's new version. A conflict
43//! leaves the update unfinished, waiting in `.devset/conflicts/`: a chain under
44//! [`Mode::Continue`] installs the fix, or a [`Rollback`] takes the whole update back.
45//!
46//! # Types
47//!
48//! - [`Target`]: the directory devset manages, and what its `.devset/` records.
49//! - [`Resolved`]: the target's layers composed, one provider per path.
50//! - [`Survey`]: every managed path as the profile wants it, as recorded, and as on disk.
51//! - [`Plan`]: what committing does to each path.
52//! - [`Rollback`]: an unfinished update, and what taking it back restores.
53//! - [`Error`]: why any of it did not go through, one type per domain.
54//!
55//! The files people write have modules of their own: [`profile`] for `profile.toml`,
56//! [`target`] for `.devset/config.toml`, and [`source`] for where a layer comes from.
57
58extern crate alloc;
59
60pub mod plan;
61pub mod profile;
62pub mod resolve;
63pub mod source;
64pub mod survey;
65pub mod target;
66
67mod commit;
68mod digest;
69mod errors;
70mod format;
71mod git;
72mod merge;
73mod part;
74mod path;
75mod rollback;
76mod settings;
77mod tree;
78mod vars;
79
80pub use crate::commit::commit;
81pub use crate::errors::{
82 Error, MergeError, ParseError, PathError, ProfileError, Result, SourceError, TargetError,
83 VarError,
84};
85pub use crate::path::RelPath;
86#[doc(inline)]
87pub use crate::plan::{Mode, Plan, plan};
88#[doc(inline)]
89pub use crate::resolve::{Refresh, Resolved, resolve};
90pub use crate::rollback::{Revert, Rollback};
91#[doc(inline)]
92pub use crate::source::Cache;
93#[doc(inline)]
94pub use crate::survey::{Survey, survey};
95#[doc(inline)]
96pub use crate::target::Target;
97
98/// This build's version, which a profile's `devset` requirement is checked against.
99pub const VERSION: &str = env!("CARGO_PKG_VERSION");