10 个版本

0.4.0 2020 年 12 月 21 日
0.3.2 2020 年 2 月 17 日
0.3.1 2020 年 1 月 9 日
0.2.4 2020 年 1 月 9 日
0.1.0 2020 年 1 月 3 日

#87 in 缓存

Apache-2.0

285KB
5K SLoC

Crates.io

DAG 感知工件构建器

Rust 包用于管理以有向无环图(DAG)方式连接的工件构建和缓存,即工件可能依赖于其他工件。

此包提供的缓存功能在工件构建器使用可消耗资源、构建过程是重量级操作或需要在构建器之间正确保留给定的 DAG 依赖结构时特别有用。

最小 Rust 版本:1.40

基本概念

daab 的基本概念围绕着 构建器,构建器是用户提供的实现 Builder 特性的结构体。该特性本质上有一个关联类型 Artifact 和一个方法 build,后者将生成一个 Artifact 类型的值,随后将被称为 工件。为了能够依赖其他构建器的工件,build 方法还提供了一个 Resolver,允许检索其他工件的工件。

为了允许构建器和工件形成一个有向无环图,此包在其核心提供了一个工件 Cache,用于保存构建器的工件,以防止构建器生成多个相同的工件。因此,不同的构建器可以依赖相同的构建器并从 Cache 获取相同的工件。

为了能够共享构建器和工件,此包还提供了一个 的概念,在最基本的情况下,它们分别是一个不可见的 Rc<dyn Any> 和一个透明的 Rc<T>。这些是通过例如 Cache 的泛型参数引用的。有关更多详细信息,请参阅 canning 模块。

除了罐装之外,Cache 还期望构建器被包裹在一个不可见的 Blueprint 中,以强制封装,即它阻止用户访问内部结构(实现 Builder 特性的结构),而只允许 Cache 本身调用其 build 方法。

入门指南

对于基本概念(如上所述),存在简化特性,跳过了更高级的功能。其中之一是rc模块中的SimpleBuilder,它使用Rc进行缓存,并为所有上述类型提供了简化的别名(最小泛型参数)。要开始使用,rc模块可能是最佳起点。

示例

use std::rc::Rc;
use daab::*;

// Simple artifact
#[derive(Debug)]
struct Leaf {
    //...
}

// Simple builder
#[derive(Debug)]
struct BuilderLeaf {
    // ...
}
impl BuilderLeaf {
    pub fn new() -> Self {
        Self {
            // ...
        }
    }
}
impl rc::SimpleBuilder for BuilderLeaf {
    type Artifact = Leaf;

    fn build(&self, _resolver: &mut rc::Resolver) -> Self::Artifact {
        Leaf{
            // ...
        }
    }
}

// Composed artifact, linking to a Leaf
#[derive(Debug)]
struct Node {
    leaf: Rc<Leaf>, // Dependency artifact
    value: u8, // Some custom value
    // ...
}

// Composed builder, depending on BuilderLeaf
#[derive(Debug)]
struct BuilderNode {
    builder_leaf: rc::Blueprint<BuilderLeaf>, // Dependency builder
    // ...
}
impl BuilderNode {
    pub fn new(builder_leaf: rc::Blueprint<BuilderLeaf>) -> Self {
        Self {
            builder_leaf,
            // ...
        }
    }
}
use std::any::Any;
impl rc::Builder for BuilderNode {
    type Artifact = Node;
    type DynState = u8;
    type Err = Never;

    fn build(&self, resolver: &mut rc::Resolver<Self::DynState>) -> Result<Rc<Self::Artifact>, Never> {
        // Resolve Blueprint to its artifact
        // Unpacking because the Err type is Never.
        let leaf = resolver.resolve(&self.builder_leaf).unpack();

        Ok(Node {
            leaf,
            value: *resolver.my_state(),
            // ...
        }.into())
    }
    fn init_dyn_state(&self) -> Self::DynState {
        42
    }
}

// The cache to storing already created artifacts
let mut cache = rc::Cache::new();

// Constructing builders
let leaf_builder = rc::Blueprint::new(BuilderLeaf::new());

let node_builder_1 = rc::Blueprint::new(BuilderNode::new(leaf_builder.clone()));
let node_builder_2 = rc::Blueprint::new(BuilderNode::new(leaf_builder.clone()));

// Using the cache to access the artifacts from the builders

// The same builder results in same artifact
assert!(Rc::ptr_eq(&cache.get(&node_builder_1).unpack(), &cache.get(&node_builder_1).unpack()));

// Different builders result in different artifacts
assert!( ! Rc::ptr_eq(&cache.get(&node_builder_1).unpack(), &cache.get(&node_builder_2).unpack()));

// Different artifacts may link the same dependent artifact
assert!(Rc::ptr_eq(&cache.get(&node_builder_1).unpack().leaf, &cache.get(&node_builder_2).unpack().leaf));

// Purge builder 2 to ensure the following does not affect it
cache.purge(&node_builder_2);

// Test dynamic state
assert_eq!(cache.get(&node_builder_1).unpack().value, 42);

// Change state
*cache.dyn_state_mut(&node_builder_1) = 127.into();
// Without invalidation, the cached artefact remains unchanged
assert_eq!(cache.dyn_state(&node_builder_1), &127);
// Invalidate node, and ensure it made use of the state
assert_eq!(cache.get(&node_builder_1).unpack().value, 127);

// State of node 2 remains unchanged
assert_eq!(cache.get_dyn_state(&node_builder_2), None);
assert_eq!(cache.get(&node_builder_2).unpack().value, 42);

调试

daab附带丰富的调试工具。然而,为了将生产影响降至最低,调试功能被封装在diagnostics特性之后。

当然,调试特性是为了让此crate的用户调试他们的图。因此,它更像是一个诊断特性(因此得名)。诊断由一个Doctor执行,这是一个接收各种内部事件以记录、打印或以其他方式帮助处理错误的特性。

已采取措施确保diagnostics特性具有广泛的适用性,同时保持非diagnostics API与diagnostics-API的兼容性,这意味着未使用diagnostics特性的项目可以轻松转换为使用diagnostics,通常只需将Cache::new()替换为Cache::new_with_doctor()即可。为了存储DoctorCache是泛型到医生,这对于其创建和按值存储很重要。其余时间,Cache使用dyn Doctor作为其默认泛型参数。为了便于在它们之间进行转换,所有可创建的Cache(即不是Cache<dyn Doctor>)都实现了DerefMut&mut Cache<dyn Doctor>,它实现了所有重要的方法。

特性

此crate提供以下特性

  • diagnostics启用了详尽的图和缓存交互调试。它向Cache添加了new_with_doctor()函数,并添加了带有Doctor特性定义和一些默认Doctordiagnostics模块。

  • tynm启用对tynm crate的可选依赖,该crate添加了缩写类型名的功能,一些默认的Doctor使用它,因此它仅与diagnostics特性一起使用。

  • unsized启用通过BlueprintUnsized::into_unsized在无尺寸构建器之间进行更好的转换。此特性需要Nightly Rust

许可

根据Apache License,版本2.0许可(LICENSEhttps://apache.ac.cn/licenses/LICENSE-2.0)。

贡献

除非您明确声明,否则您根据Apache-2.0许可证定义的任何有意提交以包含在本项目中的贡献,应按上述方式许可,不附加任何额外条款或条件。

依赖项

~195KB