用于根据标记

时间:2016-04-06 07:51:37

标签: rust api-design

我有一个C库,使用起来有点笨拙,我试图以安全,惯用和符合人体工程学的方式包装Rust API。

为了使这个问题更容易理解,而不是通过C API的所有血腥细节,下面是一个稍微好一点的Rust版本的API,但仍需要包装才能符合人体工程学使用。

struct Authorization {
    // private
}

struct Right {
    name: String,
    value: Vec<u8>,
    authorized: bool,
}

enum Flags {
    InteractionAllowed = (1 << 0),
    ExtendRights = (1 << 1),
    PartialRights = (1 << 2),
    // ...etc...
}

enum Error {
    Denied,
    Cancelled,
    InteractionNotAllowed,
    // ...etc...
}

impl Authorization {
    fn new() -> Self;
    fn authorize(&mut self, rights: &[Right], flags: Flags) -> Result<Vec<Right>, Error>;
}

这个想法是你创建这个不透明的授权引用,然后可以通过提示用户输入密码或确认来查询当前是否授权某些权限,或者要求它授权这些权限。每个权利都包含名称和值。这组名称是可扩展的;用户可以定义自己的权限。价值取决于名称;它可以是任意的序列化数据。

标志决定了你正在做什么样的操作。如果您没有传递任何标志,它只会检查所请求的权限当前是否已获得授权,如果是,则返回Ok(_)。如果您添加ExtendRights标志,它会尽可能为您的流程提供新的权限。如果添加InteractionAllowed标志,则可能会在扩展权限之前提示用户输入密码或进行确认。如果添加PartialRights标志,即使未授予所有请求的权限,它也会成功,并且返回的Vec<Right>将使用authorized标志指示授予哪些权限。

现在,这个API使用起来有点笨拙,因为你需要使用字符串常量来引用权限,序列化和反序列化任何可能与你自己相关的value数据,没有类型安全,并且实际API实际上包含一个额外的environment个名称/值对切片,您需要传递这些切片以提供一些其他信息。

为了使这个API更友好,我已经围绕它建立了一个构建器API,以及一个允许定义不同类型的权限并传入相关数据的特征。

如下所示,用于构建和检查

pub trait Right {
    fn name(&self) -> &str;
    fn value(&self) -> Vec<u8> {
        vec![]
    }
}

pub struct Execute;

impl Right for Execute {
    fn name(&self) -> &str {
        "com.example.execute"
    }
}

pub struct MyCustomRight {
    authorized_dirs: Vec<Path>,
}

impl Right for MyCustomRight {
    fn name(&self) -> &str {
        "com.example.custom"
    }
    fn value(&self) -> Vec<u8> {
        let mut buf = Vec::new();
        let mut serializer = some_serializer(&mut buf);
        self.authorized_dirs.serialize(serializer);
        buf
    }
}

impl Builder {
    fn new() -> Self;
    fn right<R>(&mut self, right: R) -> &mut Self;
    fn extend_rights(&mut self) -> &mut Self;
    fn interaction_allowed(&mut self) -> &mut Self;
    fn check(&mut self) -> Result<(), Error>;
}

如果您只是想检查授权是否授权

,那么您可以使用API
Builder::new()
    .interaction_allowed()
    .right(Execute)
    .right(MyCustomRight { authorized_dirs: vec![] })
    .check();

到目前为止,仅仅检查一组给定的权限是否被授权(可能是通过交互),这个API运行良好; trait允许您实现自己的自定义权限,并将权限作为数据结构传递,因此能够轻松封装序列化。

现在,我想扩展API以支持部分授权案例;如果允许一组权限,我希望能够传递PartialRights标志并获得一些可以授权的权限集,而不仅仅是全部或全部检查,包括反序列化的版本他们的相关价值观。

问题是,如何最好地返回多种不同类型中的一种,这些类型的用户可以扩展(因此可以提前定义枚举,嵌入一组固定类型在它)。

我想到的可能性很少,但它们在某种程度上看起来有点麻烦:

  • 对于大多数这些,您可以在调用right时指定一个特征对象来指定查询权限,这样就能够将值反序列化为适当的类型并对其执行某些操作。
  • 返回Right特征的特征对象的向量,使用您可能想要对该值执行的任何操作进行扩展。好像那时你需要预测用户想要对这个值做的每件事情,这很麻烦。
  • 返回Box<Any>的向量,让客户端尝试向下转发他们关心的每种Right类型。
  • 添加一个带有闭包的right调用版本,该调用将返回Right,并返回有关是否已获得授权的信息。
  • 只需获取可变传递的权限,并在其上设置authorized标记,这样您就可以为您事先关心的每个权限定义一个变量,将其传入,并在之后进行检查。
  • 拥有自己的自定义enum,并实施某种方式将您关注的所有类型转换为自定义enum

我很难找到一个好的设计,允许下游的包装盒定义自己的权利和相关的数据(包括序列化和反序列化),然后收回一些东西,这样可以很容易地检查哪些权利被授权出你所关心的那套。关于此API如何在实践中发挥作用的建议和草图表示赞赏。

如果我在上面的API简化中尝试过多地从原始问题中抽象出来,here is an excerpt of the raw C bindings, and my current wrapper around it

0 个答案:

没有答案