os_checker_types/
layout.rs

1use crate::{prelude::*, CheckerTool, XString};
2use cargo_metadata::Metadata;
3use std::fmt;
4
5/// The json serialized from cargo meta_data.
6/// This solves the binary encoding/decoding problem
7/// when [some serde attrs like skip are not supported][serde problem].
8///
9/// [serde problem]: https://docs.rs/bincode/2.0.0-rc.3/bincode/serde/index.html#known-issues
10#[derive(Serialize, Deserialize, Encode, Decode)]
11pub struct CargoMetaData {
12    pub meta_data: String,
13}
14
15impl CargoMetaData {
16    pub fn meta_data(&self) -> serde_json::Result<Metadata> {
17        serde_json::from_str(&self.meta_data)
18    }
19
20    pub fn from_meta_data(meta_data: &Metadata) -> serde_json::Result<Self> {
21        serde_json::to_string(meta_data).map(|meta_data| Self { meta_data })
22    }
23}
24
25pub type Workspaces = IndexMap<Utf8PathBuf, CargoMetaData>;
26
27#[derive(Encode, Decode, Default)]
28pub struct CacheLayout {
29    /// 仓库根目录的完整路径,可用于去除 Metadata 中的路径前缀,让路径看起来更清爽
30    #[musli(with = musli::serde)]
31    pub root_path: Utf8PathBuf,
32    /// 所有 Cargo.toml 的路径
33    ///
34    /// NOTE: Cargo.toml 并不意味着对应于一个 package —— virtual workspace 布局无需定义
35    ///       `[package]`,因此要获取所有 packages 的信息,应使用 [`Layout::packages`]
36    #[musli(with = musli::serde)]
37    pub cargo_tomls: Box<[Utf8PathBuf]>,
38    #[musli(with = musli::serde)]
39    pub workspaces: Workspaces,
40    /// The order is by pkg name and dir path.
41    #[musli(with = musli::serde)]
42    pub packages_info: Box<[CachePackageInfo]>,
43    pub resolves: Box<[CacheResolve]>,
44}
45
46redb_value!(CacheLayout, name: "OsCheckerCacheLayout",
47    read_err: "Not a valid cache layout.",
48    write_err: "Cache layout can't be encoded to bytes."
49);
50
51impl fmt::Debug for CacheLayout {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        f.debug_struct("CacheLayout")
54            .field("root_path", &self.root_path)
55            .field("cargo_tomls", &self.cargo_tomls)
56            // .field("workspaces.len", &self.workspaces.len())
57            .field("packages_info", &self.packages_info)
58            .finish()
59    }
60}
61
62/// Refer to https://github.com/os-checker/os-checker/issues/26 for more info.
63// FIXME: 把 tag 和 path 分开
64// TODO: 在明确指定 targets 的情况下,还需要脚本指定的 targets 吗?(关于安装和 resolve)
65#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
66pub enum TargetSource {
67    RustToolchainToml(Utf8PathBuf),
68    CargoConfigToml(Utf8PathBuf),
69    CargoTomlDocsrsInPkgDefault(Utf8PathBuf),
70    CargoTomlDocsrsInWorkspaceDefault(Utf8PathBuf),
71    CargoTomlDocsrsInPkg(Utf8PathBuf),
72    CargoTomlDocsrsInWorkspace(Utf8PathBuf),
73    DetectedByPkgScripts(Utf8PathBuf),
74    DetectedByRepoGithub(Utf8PathBuf),
75    DetectedByRepoScripts(Utf8PathBuf),
76    // 被配置文件指定
77    SpecifiedInOsCheckerConfig,
78    /// 非上面的方式指定,那么默认会增加一个 host target
79    UnspecifiedDefaultToHostTarget,
80}
81
82impl TargetSource {
83    pub fn descibe(&self) -> (&'static str, Option<&Utf8Path>) {
84        match self {
85            TargetSource::RustToolchainToml(p) => ("RustToolchainToml", Some(p)),
86            TargetSource::CargoConfigToml(p) => ("CargoConfigToml", Some(p)),
87            TargetSource::CargoTomlDocsrsInPkgDefault(p) => {
88                ("CargoTomlDocsrsInPkgDefault", Some(p))
89            }
90            TargetSource::CargoTomlDocsrsInWorkspaceDefault(p) => {
91                ("CargoTomlDocsrsInWorkspaceDefault", Some(p))
92            }
93            TargetSource::CargoTomlDocsrsInPkg(p) => ("CargoTomlDocsrsInPkg", Some(p)),
94            TargetSource::CargoTomlDocsrsInWorkspace(p) => ("CargoTomlDocsrsInWorkspace", Some(p)),
95            TargetSource::DetectedByPkgScripts(p) => ("DetectedByPkgScripts", Some(p)),
96            TargetSource::DetectedByRepoGithub(p) => ("DetectedByRepoGithub", Some(p)),
97            TargetSource::DetectedByRepoScripts(p) => ("DetectedByRepoScripts", Some(p)),
98            TargetSource::SpecifiedInOsCheckerConfig => ("SpecifiedInOsCheckerConfig", None),
99            TargetSource::UnspecifiedDefaultToHostTarget => {
100                ("UnspecifiedDefaultToHostTarget", None)
101            }
102        }
103    }
104}
105
106/// A list of target triples obtained from multiple sources.
107/// The orders in key and value demonstrates how they shape.
108#[derive(Debug, Default, Serialize, Deserialize)]
109pub struct Targets {
110    pub map: IndexMap<String, Vec<TargetSource>>,
111}
112
113#[derive(Debug, Serialize, Deserialize)]
114pub struct CachePackageInfo {
115    pub pkg_name: XString,
116    /// i.e. manifest_dir
117    pub pkg_dir: Utf8PathBuf,
118    pub targets: Targets,
119    pub channel: String,
120}
121
122#[derive(Debug, Encode, Decode)]
123pub struct CacheResolve {
124    #[musli(with = musli::serde)]
125    pub pkg_name: XString,
126    pub target: String,
127    /// 仅当自定义检查命令出现 --target 时为 true
128    pub target_overridden: bool,
129    pub features_args: Vec<String>,
130    pub channel: String,
131    pub checker: CheckerTool,
132    /// 完整的检查命令字符串(一定包含 --target):
133    /// 来自 os-checker 生成或者配置文件自定义
134    pub cmd: String,
135}
136
137#[derive(Serialize, Deserialize, Debug)]
138pub struct ListTargets {
139    pub user: XString,
140    pub repo: XString,
141    pub pkg: XString,
142    pub targets: Vec<String>,
143}
144
145#[test]
146fn workspaces() {
147    #[derive(Encode, Decode)]
148    struct Meta {
149        #[musli(with=musli::serde)]
150        data: Metadata,
151    }
152
153    let metadata = cargo_metadata::MetadataCommand::new()
154        .manifest_path("../Cargo.toml")
155        .exec()
156        .unwrap();
157
158    let meta = Meta { data: metadata };
159    let _bytes = musli::storage::to_vec(&meta).unwrap();
160
161    // thread 'layout::workspaces' panicked at os-checker-types\src\layout.rs:105:54:
162    // called `Result::unwrap()` on an `Err` value: Error { err: Message("Skipping is
163    // not supported, expected type supported by the storage decoder") }
164    // let _: Meta = musli::storage::from_slice(&bytes).unwrap();
165
166    let metadata = meta.data;
167
168    let meta = CargoMetaData {
169        meta_data: serde_json::to_string_pretty(&metadata).unwrap(),
170    };
171    let bytes = musli::storage::to_vec(&meta).unwrap();
172    let meta_string: CargoMetaData = musli::storage::from_slice(&bytes).unwrap();
173    assert!(meta_string.meta_data().is_ok());
174}