This is an automated email from the ASF dual-hosted git repository.

psiace pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal-oli.git


The following commit(s) were added to refs/heads/main by this push:
     new bb2c686  feat: implement `config view` (#9)
bb2c686 is described below

commit bb2c686a170c3286a809bcfb304964db458606ea
Author: Ruihang Xia <[email protected]>
AuthorDate: Wed Nov 26 11:48:29 2025 +0800

    feat: implement `config view` (#9)
    
    Signed-off-by: Ruihang Xia <[email protected]>
---
 Cargo.lock             | 107 ++++++++++++++++++++++++++++++++++++++++++++++++-
 Cargo.toml             |   1 +
 src/commands/config.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++
 src/commands/mod.rs    |   3 ++
 src/config/mod.rs      |   8 ++++
 5 files changed, 218 insertions(+), 1 deletion(-)

diff --git a/Cargo.lock b/Cargo.lock
index ab7ce87..db1aa01 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -331,7 +331,7 @@ version = "7.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a"
 dependencies = [
- "crossterm",
+ "crossterm 0.28.1",
  "unicode-segmentation",
  "unicode-width",
 ]
@@ -387,6 +387,15 @@ dependencies = [
  "tiny-keccak",
 ]
 
+[[package]]
+name = "convert_case"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
+dependencies = [
+ "unicode-segmentation",
+]
+
 [[package]]
 name = "core-foundation-sys"
 version = "0.8.7"
@@ -424,6 +433,24 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "crossterm"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
+dependencies = [
+ "bitflags",
+ "crossterm_winapi",
+ "derive_more",
+ "document-features",
+ "mio",
+ "parking_lot",
+ "rustix 1.0.7",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
 [[package]]
 name = "crossterm_winapi"
 version = "0.9.1"
@@ -469,6 +496,27 @@ dependencies = [
  "powerfmt",
 ]
 
+[[package]]
+name = "derive_more"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "difflib"
 version = "0.4.0"
@@ -534,6 +582,21 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
 
+[[package]]
+name = "document-features"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
+
 [[package]]
 name = "either"
 version = "1.15.0"
@@ -1067,6 +1130,19 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "inquire"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "2628910d0114e9139056161d8644a2026be7b117f8498943f9437748b04c9e0a"
+dependencies = [
+ "bitflags",
+ "crossterm 0.29.0",
+ "dyn-clone",
+ "unicode-segmentation",
+ "unicode-width",
+]
+
 [[package]]
 name = "insta"
 version = "1.43.1"
@@ -1213,6 +1289,12 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
 
+[[package]]
+name = "litrs"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
+
 [[package]]
 name = "lock_api"
 version = "0.4.12"
@@ -1283,6 +1365,7 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
 dependencies = [
  "libc",
+ "log",
  "wasi 0.11.0+wasi-snapshot-preview1",
  "windows-sys 0.52.0",
 ]
@@ -1373,6 +1456,7 @@ dependencies = [
  "humantime",
  "humantime-serde",
  "indicatif",
+ "inquire",
  "insta",
  "insta-cmd",
  "opendal",
@@ -2184,6 +2268,27 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
+[[package]]
+name = "signal-hook"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-mio"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook",
+]
+
 [[package]]
 name = "signal-hook-registry"
 version = "1.4.5"
diff --git a/Cargo.toml b/Cargo.toml
index 52305b2..66a56b0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,6 +38,7 @@ humansize = { version = "2.1" }
 humantime = { version = "2.2" }
 humantime-serde = { version = "1.1" }
 indicatif = { version = "0.18" }
+inquire = { version = "0.9", default-features = false, features = 
["crossterm"] }
 opendal = { version = "0.54.0", features = [
   "services-azblob",
   "services-azdls",
diff --git a/src/commands/config.rs b/src/commands/config.rs
new file mode 100644
index 0000000..b543c34
--- /dev/null
+++ b/src/commands/config.rs
@@ -0,0 +1,100 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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::HashMap;
+
+use anyhow::Result;
+use inquire::Select;
+use inquire::error::InquireError;
+
+use crate::config::Config;
+use crate::params::config::ConfigParams;
+
+#[derive(Debug, clap::Parser)]
+#[command(
+    name = "config",
+    about = "Manage oli configuration",
+    disable_version_flag = true
+)]
+pub struct ConfigCmd {
+    #[command(subcommand)]
+    subcommand: ConfigSubcommand,
+}
+
+#[derive(Debug, clap::Subcommand)]
+enum ConfigSubcommand {
+    View(ConfigViewCmd),
+}
+
+impl ConfigCmd {
+    pub fn run(self) -> Result<()> {
+        match self.subcommand {
+            ConfigSubcommand::View(cmd) => cmd.run(),
+        }
+    }
+}
+
+#[derive(Debug, clap::Args)]
+#[command(name = "view", about = "Inspect configured profiles")]
+pub struct ConfigViewCmd {
+    #[command(flatten)]
+    pub config_params: ConfigParams,
+}
+
+impl ConfigViewCmd {
+    pub fn run(self) -> Result<()> {
+        let cfg = Config::load(&self.config_params.config)?;
+        let mut profiles = cfg.profile_names();
+
+        if profiles.is_empty() {
+            println!(
+                "No configuration profiles found in {}",
+                self.config_params.config.display()
+            );
+            return Ok(());
+        }
+
+        profiles.sort();
+        let selected = match Select::new("Select a profile to view", 
profiles).prompt() {
+            Ok(profile) => profile,
+            Err(InquireError::OperationCanceled | 
InquireError::OperationInterrupted) => {
+                return Ok(());
+            }
+            Err(err) => return Err(err.into()),
+        };
+
+        let Some(options) = cfg.profile(&selected) else {
+            // Profiles are loaded up front, so missing here would indicate a 
race.
+            println!("Profile `{selected}` is no longer available.");
+            return Ok(());
+        };
+
+        println!("Profile: {selected}");
+        println!("--------------------------------");
+        for (k, v) in ordered_entries(options) {
+            println!("{k} = {v}");
+        }
+
+        Ok(())
+    }
+}
+
+fn ordered_entries(options: &HashMap<String, String>) -> Vec<(&String, 
&String)> {
+    let mut entries = options.iter().collect::<Vec<_>>();
+    entries.sort_by(|a, b| a.0.cmp(b.0));
+    entries
+}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 1dd7182..5696aff 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -19,6 +19,7 @@
 
 pub mod bench;
 pub mod cat;
+pub mod config;
 pub mod cp;
 pub mod edit;
 pub mod ls;
@@ -33,6 +34,7 @@ pub enum OliSubcommand {
     Cat(cat::CatCmd),
     Cp(cp::CopyCmd),
     Edit(edit::EditCmd),
+    Config(config::ConfigCmd),
     Ls(ls::LsCmd),
     Rm(rm::RmCmd),
     Stat(stat::StatCmd),
@@ -47,6 +49,7 @@ impl OliSubcommand {
             Self::Cat(cmd) => cmd.run(),
             Self::Cp(cmd) => cmd.run(),
             Self::Edit(cmd) => cmd.run(),
+            Self::Config(cmd) => cmd.run(),
             Self::Ls(cmd) => cmd.run(),
             Self::Rm(cmd) => cmd.run(),
             Self::Stat(cmd) => cmd.run(),
diff --git a/src/config/mod.rs b/src/config/mod.rs
index d6e467d..4c493e7 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -163,6 +163,14 @@ impl Config {
         Ok((op, path))
     }
 
+    pub fn profile_names(&self) -> Vec<String> {
+        self.profiles.keys().cloned().collect()
+    }
+
+    pub fn profile(&self, name: &str) -> Option<&HashMap<String, String>> {
+        self.profiles.get(name)
+    }
+
     pub fn operator(&self, profile_name: &str) -> Result<Operator> {
         let profile = self
             .profiles

Reply via email to