diff --git a/Cargo.toml b/Cargo.toml index ad2c6dcc..5e705eae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "examples", "foyer", "foyer-bench", + "foyer-cli", "foyer-common", "foyer-intrusive", "foyer-memory", diff --git a/codecov.yml b/codecov.yml index 7863ed78..909bbf9d 100644 --- a/codecov.yml +++ b/codecov.yml @@ -21,4 +21,5 @@ coverage: informational: true only_pulls: true ignore: - - "foyer-util" \ No newline at end of file + - "foyer-util" + - "foyer-cli" \ No newline at end of file diff --git a/foyer-cli/Cargo.toml b/foyer-cli/Cargo.toml new file mode 100644 index 00000000..8d89b788 --- /dev/null +++ b/foyer-cli/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "foyer-cli" +version = "0.0.0" +edition = "2021" +authors = ["MrCroxx "] +description = "Hybrid cache for Rust" +license = "Apache-2.0" +repository = "https://github.com/foyer-rs/foyer" +homepage = "https://github.com/foyer-rs/foyer" +readme = "../README.md" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +bytesize = "1" +clap = { workspace = true } +thiserror = "1" + +[dev-dependencies] + +[[bin]] +name = "foyer" +path = "src/main.rs" diff --git a/foyer-cli/src/args/error.rs b/foyer-cli/src/args/error.rs new file mode 100644 index 00000000..4bd96da4 --- /dev/null +++ b/foyer-cli/src/args/error.rs @@ -0,0 +1,32 @@ +// Copyright 2024 Foyer Project Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +/// Disk cache error type. +#[derive(thiserror::Error, Debug)] +pub enum Error { + /// I/O error. + #[error("io error: {0}")] + Io(#[from] std::io::Error), + /// `fio` not available. + #[error("fio not available")] + FioNotAvailable, + /// Other error. + #[error(transparent)] + Other(#[from] anyhow::Error), +} + +/// Disk cache result type. +pub type Result = core::result::Result; diff --git a/foyer-cli/src/args/fio.rs b/foyer-cli/src/args/fio.rs new file mode 100644 index 00000000..5a087bdd --- /dev/null +++ b/foyer-cli/src/args/fio.rs @@ -0,0 +1,67 @@ +// Copyright 2024 Foyer Project Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{collections::HashSet, process::Command}; + +use anyhow::anyhow; + +use crate::args::error::{Error, Result}; + +type IoEngine = String; + +#[derive(Debug)] +pub struct Fio { + io_engines: HashSet, +} + +impl Fio { + pub fn init() -> Result { + if !Self::available() { + return Err(Error::FioNotAvailable); + } + + let io_engines = Self::list_io_engines()?; + + Ok(Self { io_engines }) + } + + fn available() -> bool { + let output = match Command::new("fio").arg("--version").output() { + Ok(output) => output, + Err(_) => return false, + }; + output.status.success() + } + + pub fn io_engines(&self) -> &HashSet { + &self.io_engines + } + + fn list_io_engines() -> Result> { + let output = Command::new("fio").arg("--enghelp").output()?; + if !output.status.success() { + return Err(anyhow!("fail to get available io engines with fio").into()); + } + + let io_engines = String::from_utf8_lossy(&output.stdout) + .split('\n') + .skip(1) + .map(|s| s.trim()) + .filter(|s| !s.is_empty()) + .map(String::from) + .collect(); + + Ok(io_engines) + } +} diff --git a/foyer-cli/src/args/mod.rs b/foyer-cli/src/args/mod.rs new file mode 100644 index 00000000..5c5e4dd1 --- /dev/null +++ b/foyer-cli/src/args/mod.rs @@ -0,0 +1,48 @@ +// Copyright 2024 Foyer Project Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod error; +mod fio; + +use bytesize::ByteSize; +use clap::{ArgGroup, Args}; +use fio::Fio; + +#[derive(Debug, Args)] +#[command(group = ArgGroup::new("exclusive").required(true).args(&["file", "dir"]))] +pub struct ArgsArgs { + /// File for disk cache data. Use `DirectFile` as device. + /// + /// Either `file` or `dir` must be set. + #[arg(short, long)] + file: Option, + + /// Directory for disk cache data. Use `DirectFs` as device. + /// + /// Either `file` or `dir` must be set. + #[arg(short, long)] + dir: Option, + + /// Size of the disk cache occupies. + #[arg(short, long)] + size: Option, +} + +pub fn run(args: ArgsArgs) { + println!("{args:#?}"); + + let fio = Fio::init().unwrap(); + + println!("{:#?}", fio.io_engines()); +} diff --git a/foyer-cli/src/main.rs b/foyer-cli/src/main.rs new file mode 100644 index 00000000..da1dee74 --- /dev/null +++ b/foyer-cli/src/main.rs @@ -0,0 +1,41 @@ +// Copyright 2024 Foyer Project Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! `foyer-cli` provides debug tools for foyer, + +mod args; + +use args::ArgsArgs; +use clap::{Parser, Subcommand}; + +#[derive(Debug, Parser)] +#[command(author, version, about)] +pub struct Cli { + #[command(subcommand)] + command: Command, +} + +#[derive(Debug, Subcommand)] +pub enum Command { + /// Automatic arguments detector. + Args(ArgsArgs), +} + +fn main() { + let cli = Cli::parse(); + + match cli.command { + Command::Args(args) => args::run(args), + } +}