Crate diskplan_schema
source ·Expand description
This crate provides the means to constuct a tree of SchemaNodes from text form (see parse_schema).
The language of the text form uses significant whitespace (four spaces) for indentation,
distinguishes between files and directories by the presence of a /
, and whether
this is a symlink by presence of an ->
(followed by its target path expression).
That is, each indented node of the directory tree takes one of the following forms:
Syntax | Description |
---|---|
str | A file |
str/ | A directory |
str -> expr | A symlink to a file |
str/ -> expr | A symlink to a directory |
Properties of a given node are set using the following tags:
Tag | Types | Description |
---|---|---|
:owner expr | All | Sets the owner of this file/directory/symlink target |
:group expr | All | Sets the group of this file, directory or symlink target |
:mode octal | All | Sets the permissions of this file/directory/symlink target |
:source expr | File | Copies content into this file from the path given by expr |
:let ident = expr | Directory | Sets a variable at this level to be used by deeper levels |
:def ident | Directory | Defines a sub-schema that can be reused by :use |
:use ident | Directory | Reuses a sub-schema defined by :def |
Simple Schema
The top level of a schema describes a directory, whose attributes may be set by :owner
, :group
and :mode
tags:
use diskplan_schema::*;
let schema_root = parse_schema("
:owner person
:group user
:mode 777
")?;
assert!(matches!(schema_root.schema, SchemaType::Directory(_)));
assert_eq!(schema_root.attributes.owner.unwrap(), "person");
assert_eq!(schema_root.attributes.group.unwrap(), "user");
assert_eq!(schema_root.attributes.mode.unwrap(), 0o777);
A DirectorySchema may contain sub-directories and files:
// ...
"
subdirectory/
:owner admin
:mode 700
file_name
:source content/example_file
"
// ...
assert_eq!(
parse_schema(text)?
.schema
.as_directory()
.expect("Not a directory")
.entries()
.len(),
2
);
It may also contain symlinks to directories and files, whose own schemas will apply to the target:
// ...
"
example_link/ -> /another/disk/example_target/
:owner admin
:mode 700
file_to_create_at_target_end
:source content/example_file
"
// ...
let (binding, node) = directory.entries().first().unwrap();
assert!(matches!(
binding,
Binding::Static(ref name) if name == &String::from("example_link")
));
assert_eq!(
node.symlink.as_ref().unwrap().to_string(),
String::from("/another/disk/example_target/")
);
assert!(matches!(node.schema, SchemaType::Directory(_)));
Variable Substitution
Variables can be used to drive construction, for example:
"
:let asset_type = character
:let asset_name = Monkey
assets/
$asset_type/
$asset/
reference/
"
Variables will also pick up on names already on disk (even if a :let
provides a different
value). For example, if we had assets/prop/Banana
on disk already, $asset_type
would match
against and take the value “prop” (as well as “character”) and $asset
would take the value
“Banana” (as well as “Monkey”), producing:
assets
├── character
│ └── Monkey
│ └── reference
└── prop
└── Banana
└── reference
Pattern Matching
Any node of the schema can have a :match
tag, which, via a Regular Expression, controls the
possible values a variable can take.
IMPORTANT: No two variables can match the same value. If they do, an error will occur during
execution, so be careful to ensure there is no overlap between patterns. The use of :avoid
can help restrict the pattern matching and ensure proper partitioning.
Static names (without variables) always take precedence and do not need to be unique with respect to variable patterns (and vice versa).
For example, this is legal in the schema but will always error in practice:
$first/
$second/
For instance, when operating on the path /test
, it yields:
Error: "test" matches multiple dynamic bindings "$first" and "$second" (Any)
A working example might be:
$first/
:match [A-Z].*
$second/
:match [^A-Z].*
Schema Reuse
Portions of a schema can be built from reusable definitions.
A definition is formed using the :def
keyword, followed by its name and a body like any
other schema node:
:def reusable/
anything_inside/
It is used by adding the :use
tag inside any other (same or deeper level) node:
reused_here/
:use reusable
Multiple :use
tags may be used. Attributes are resolved in the following order:
example/
## Attributes set here win (before or after any :use lines)
:owner root
## First :use is next in precedence
:use one
## Subsequent :use lines take lower precedence
:use two
Structs
- Owner, group and UNIX permissions
- A DirectorySchema is a container of variables, definitions (named schemas) and a directory listing
- A string expression made from one or more
Token
s - A description of a file
- The name given to a variable
- A detailed error for an issue encountered during parsing
- A node in an abstract directory hierarchy
Enums
- How an entry is bound in a schema, either to a static fixed name or to a variable
- File/directory specific aspects of a node in the tree
- A choice of built-in variables that are used to provide context information during traversal
- Part of an
Expression
; a constant string, or a variable for later expansion to a string
Functions
- Parses the given text representation into a tree of
SchemaNode
s