scaffold: CLI surface with stubbed kernel and patch verbs
Add the spectral command tree -- kernel quest/test and patch check/format/commit/create/submit/update -- with every verb wired through main to a todo!() body, plus the pieces they will share: - config: resolve the kernel tree from $SPECTRAL_KERNEL, falling back to ~/.spectral/linux, patches under ~/.spectral/patches, and confirm a tree really is one before anything uses it - git: a thin wrapper over the git binary, currently only run - error: one thiserror enum, converted once in main - patch/checkpatch and patch/maintainers: seams around the tree's own scripts/checkpatch.pl and scripts/get_maintainer.pl, so spectral never carries a stale copy of the kernel's rules 20 functions are todo!(); the #[allow(dead_code)] sites mark the items that are unreachable only until their caller is written. fmt, clippy -D warnings, and test are all clean.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
//! Where the kernel tree and the patches live.
|
||||
//!
|
||||
//! Everything else asks this module for paths, so the day a config file or
|
||||
//! `spectral init` arrives, it is the only thing that changes.
|
||||
|
||||
#![allow(dead_code)] // accessors are read once the patch verbs stop being stubs
|
||||
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
/// Environment variable that points at the kernel checkout.
|
||||
pub const ENV_KERNEL_TREE: &str = "SPECTRAL_KERNEL";
|
||||
|
||||
/// Tree used when that variable is unset, relative to `$HOME`.
|
||||
const DEFAULT_KERNEL_TREE: &str = ".spectral/linux";
|
||||
|
||||
/// Where written patches land, relative to `$HOME`.
|
||||
const DEFAULT_PATCH_DIR: &str = ".spectral/patches";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
kernel_tree: PathBuf,
|
||||
patch_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Resolve the tree from `$SPECTRAL_KERNEL`, falling back to
|
||||
/// `~/.spectral/linux`.
|
||||
///
|
||||
/// The tree is not checked for existence here — commands that need it call
|
||||
/// [`Config::require_kernel_tree`], so `kernel quest` still works before
|
||||
/// anything is cloned.
|
||||
///
|
||||
/// TODO: also read `~/.config/spectral/config.toml` once `spectral init`
|
||||
/// exists. The environment variable should keep winning over the file.
|
||||
pub fn load() -> Result<Self> {
|
||||
let home = home_dir();
|
||||
let kernel_tree = env::var_os(ENV_KERNEL_TREE)
|
||||
.map_or_else(|| home.join(DEFAULT_KERNEL_TREE), PathBuf::from);
|
||||
|
||||
Ok(Self {
|
||||
kernel_tree,
|
||||
patch_dir: home.join(DEFAULT_PATCH_DIR),
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn kernel_tree(&self) -> &Path {
|
||||
&self.kernel_tree
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn patch_dir(&self) -> &Path {
|
||||
&self.patch_dir
|
||||
}
|
||||
|
||||
/// The kernel tree, having confirmed it is one.
|
||||
pub fn require_kernel_tree(&self) -> Result<&Path> {
|
||||
if self.kernel_tree.join("scripts/checkpatch.pl").is_file() {
|
||||
Ok(&self.kernel_tree)
|
||||
} else if self.kernel_tree.exists() {
|
||||
Err(Error::NotAKernelTree {
|
||||
path: self.kernel_tree.clone(),
|
||||
})
|
||||
} else {
|
||||
Err(Error::KernelTreeMissing {
|
||||
path: self.kernel_tree.clone(),
|
||||
env: ENV_KERNEL_TREE,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn home_dir() -> PathBuf {
|
||||
env::var_os("HOME").map_or_else(|| PathBuf::from("."), PathBuf::from)
|
||||
}
|
||||
Reference in New Issue
Block a user