13 个版本 (6 个重大变更)
0.9.2 | 2024 年 6 月 18 日 |
---|---|
0.9.1 | 2024 年 6 月 18 日 |
0.8.0 | 2024 年 6 月 14 日 |
0.7.0 | 2024 年 6 月 7 日 |
0.3.0 | 2024 年 5 月 24 日 |
#1419 在 网页编程
每月 34 次下载
245KB
5.5K SLoC
XAPI Oxidized
一个用于与 XNAT 网页 API 交互的 RESTful 抽象层库。允许用户通过 Rust 以编程方式对主机进行操作。
目标是能够构建工具和自动化任务。
入门
要在您的项目中使用此库,您必须将其添加到依赖项中。最简单的方法是按照以下操作
$ cargo add oxinat --features core
重要的是要知道必须使用核心功能,因为这可以解锁基本界面。这可能会在未来迭代中更改,但到目前为止,核心仍然作为一个独立的功能。
安装后,您应该能够访问 Xnat
和 XnatBuilder
结构,以及 V1
和 V2
-- 这些是初始版本实现的结构,提供了从 XAPI 文档映射的 URI 构建访问权限。
use oxinat::Xnat;
let client = Xnat::configure("your.target.host")
.use_secure(true)
.with_username("your-username")
.with_password("your-password")
.build()
.expect_err(); // No version implementation set.
XAPI Oxidized API
版本
一个 Version
是在 core
功能中定义的特质,提供了对 XAPI URI 根组件的访问权限,并且通过实现了 URIBuilder
类型和特质,允许您根据 oxinat
内部系统逐步构建 URI。
use oxinat::{Version, ProjectUri};
fn foo<V: Version>(version: &V) -> Result<(), ()> {
version.project_data().with_id("PROJECT_ID").build()?;
Ok(())
}
在上面的 .build()
调用中,oxinat
将尝试构建一个类似 Result<String, ..>
的 URI。假设它是有效的,并且上面的情况应该是,从 Result
中解包的结果应该产生类似的内容
"{data_uri}/projects/PROJECT_ID"
{data_uri}
应该已经预先格式化。当通过 Version
的实现使用时,将会调用该类型的 fn data_uri() -> String
方法。如果需要 fn root_uri() -> String
,情况也是如此。
自定义版本
在表面层次上,单元 V1
和 V2
已经被实现来访问 UriBuilder
实现的类型和特质。建议简单地使用这些单元,但也可以定义自己的。
为此,您需要安装 derive
特性,然后完整 API 将被解锁并可用。
[dependencies]
oxinat = { version = "0.6.1", features = ["core", "derive"]}
在撰写本文时,建议使用 full
特性并继续使用整个套件。
启用 derive
后,定义自定义的 Version
应该相对简单,有一些要求。您必须调用 #[version()]
在派生类型上方,并至少声明 root_uri
属性。这告诉编译器如何在包含 URI 构建特质之前构建初始的 Version
实现impl。上述 #[version()]
声明具有一些可选的附加属性。
data_uri
定义了fn data_uri() -> String
的值。如果省略,则使用root_uri
。legacy
告诉编译器只实现针对旧版 XNAT 系统的子特质。
从版本派生的例子将是
use oxinat::{Version, ProjectUri, ExperimentUri, SubjectUri};
#[derive(Clone, Version, ProjectUri, ExperimentUri, SubjectUri)]
#[version(root_uri = "xapi", data_uri = "data", legacy = true)]
struct MyVersion;
MyVersion
.project_data()
.with_id("SOME_PROJECT")
.subjects()
.with_subject("SOME_SUBJECT")
.experiments()
.build()?
然后生成的 URI 应该看起来像这样
"data/projects/SOME_PROJECT/subjects/SOME_SUBJECT/experiments"
客户端和客户端构建器
拼图中另一个关键部分将是 Xnat<V>
和 XnatBuilder<V>
。这些类型允许您代理到/从目标 XNAT 主机的调用。如前所述
use oxinat::Xnat;
use crate::MyVersion;
let client = Xnat::configure("your.target.host")
.use_secure(true)
.with_username("your-username")
.with_password("your-password")
.with_version(MyVersion)
.build()
.expect("must build an XNAT client"); // Should produce a client.
现在,我们设置了所需的版本,以及我们想要的 URI 构建器,我们可以期望有一个合适的 Xnat
客户端。
协议
正在进行一项工作,以预定义一些您可能希望执行的一些常见操作。我们将它们定义为 协议
,其中协议与XNAT为授权用户提供资源XNAT的相关 CRUD 操作相关。
对于实现 ClientCore
和 ClientREST
特性的客户端,随着我们继续开发,这些附加特性将可用。
/// Type is able to implement CREATE requests for
/// a particular model. Upon creation, these
/// methods are expected to then return the
/// individual results, and then a
/// `Ok(Self::Model)` response if the request is
/// successful.
trait Create<M> {
/// Attempt to send a CREATE request to the
/// XNAT server for **multiple** models.
fn create_many(&self, models: M) -> Vec<PinnedFuture<'_, M>>;
/// Attempt to send a CREATE request to the
/// XNAT server for **one** model.
fn create_once(&self, model: M) -> anyhow::Result<M>;
}
/// Type is able to implement RETRIEVE requests
/// for a particular model.
trait Retrieve<M> {
/// Get all instances of a particular model
/// available to the user via the XNAT host.
async fn get_all(&self) -> anyhow::Result<Vec<M>>;
/// Get all instances of a particular model
/// using another model as the query
/// parameters for the request.
async fn get_any_from(&self, model: &M) -> anyhow::Result<Vec<M>>;
/// Get one instance of a particular model
/// using another model as the query
/// parameters for the request.
async fn get_one_from(&self, model: &M) -> anyhow::Result<M>;
}
/// Type is able to implement UPDATE or UPSERT
/// requests for a particular model.
trait Update<M> {
/// Attempt to send **multiple** UPDATE
/// requests to the XNAT host.
fn update_many(&self, models: M) -> Vec<PinnedFuture<'_, M>>;
/// Attempt to send **one** UPDATE request to
/// the XNAT host.
async fn update_once(&self, model: M) -> anyhow::Result<M>;
}
/// Type is able to implement DELETE requests for
/// a particular model.
trait Delete<M> {
/// Attempt to send **multiple** DELETE
/// requests to the XNAT host.
fn delete_many(&self, models: M) -> Vec<PinnedFuture<'_, M>>;
/// Attempt to send **one** DELETE request to
/// the XNAT host.
async fn delete_once(&self, model: M) -> anyhow::Result<M>;
}
创建
Create
特性已在某些模型的通用 Xnat 客户端上实现。然后,这些模型可用于通过此特性定义的方法来创建以下实例,或针对您的目标XNAT主机。
项目
受试者
实验
扫描
资源
根据每个模型所需的深度,您可能需要提供所有或部分标识符,以便您的目标XNAT主机了解模型应在哪里/如何创建。
例如,在 Scan
模型的案例中,您需要提供项目ID作为 project
,受试者标签或ID作为 subject
,实验标签或ID作为 experiment
,以及指定扫描的ID作为 id
。
use oxinat::{ClientCore, ClientToken, Xnat};
use oxinat::models::Scan;
use oxinat::protocols::Create;
use crate::{MyVersion, client};
let mut scan = Scan::default();
scan.id = Some(14u64);
scan.experiment = Some("EXPERIMENT_LABEL".into());
scan.subject = Some("SUBJECT_LABEL".into());
scan.project = Some("PROJECT_ID".into());
scan = client.create_once(scan).await.expect("scan must be created");
检索
Retrieve
特性已在某些模型的通用 Xnat 客户端上实现。
项目
受试者
实验
扫描
评估者
资源
插件
Retrieve
特性旨在允许您从您的目标XNAT实例中 GET 资源。
use oxinat::{ClientCore, ClientToken, Xnat}
use oxinat::models::Project;
use oxinat::protocols::Retrieve;
use crate::{MyVersion, client};
// Should retrieve all project records from your
// XNAT instance.
let found: Vec<Project> = client.get_all().await.unwrap();
// Should retrieve one project which matches the
// project ID.
let mut project = Project::default();
project.id.clone_from(&Some("SOME_PROJECT_ID".into()));
let found = client.get_one_from(&project).await.unwrap();
当执行查询时,预定义的getter会尝试首先提取相关标识符,然后消耗剩余填充的字段作为查询参数来构造请求路径。
删除
Delete
特性已针对多个模型实现。这允许这些模型用于在您的目标XNAT主机中请求对现有数据的 DELETE
调用。
项目
受试者
实验
扫描
资源
与 Create
特性类似,为了使 delete
调用成功,您必须提供所有必要标识符,以便进行有效的 DELETE
调用。没有它们,您的XNAT实例将不知道要删除哪些资源,并且作为安全网,oxinat
默认不允许此操作。
依赖关系
~8-19MB
~280K SLoC