diff --git a/crates/pile/src/command/list.rs b/crates/pile/src/command/list.rs index 8087eac..44dcd5e 100644 --- a/crates/pile/src/command/list.rs +++ b/crates/pile/src/command/list.rs @@ -1,5 +1,6 @@ use anyhow::{Context, Result}; use clap::Args; +use pile_config::Label; use pile_config::objectpath::ObjectPath; use pile_dataset::Datasets; use pile_toolbox::cancelabletask::{CancelFlag, CancelableTaskError}; @@ -12,7 +13,7 @@ use crate::{CliCmd, GlobalContext}; #[derive(Debug, Args)] pub struct ListCommand { - /// Path to query, e.g. $.flac.artist + /// Path to query, e.g. $.flac.artist (or schema field name when --schema is set) #[clap(default_value = "$")] path: String, @@ -20,6 +21,10 @@ pub struct ListCommand { #[arg(long)] invert: bool, + /// Treat path as a schema field name and resolve via schema paths + #[arg(long)] + schema: bool, + /// Path to dataset config #[arg(long, short = 'c', default_value = "./pile.toml")] config: PathBuf, @@ -44,14 +49,24 @@ impl CliCmd for ListCommand { _ctx: GlobalContext, flag: CancelFlag, ) -> Result> { - let path = ObjectPath::from_str(&self.path) - .with_context(|| format!("invalid path {:?}", self.path))?; - let path = Arc::new(path); - let ds = Datasets::open(&self.config, &self.workdir) .await .with_context(|| format!("while opening dataset for {}", self.config.display()))?; + // Resolve path arg: either schema field paths or a single ObjectPath + let schema_paths: Arc> = if self.schema { + let label = Label::new(&self.path) + .ok_or_else(|| anyhow::anyhow!("invalid schema field name {:?}", self.path))?; + let spec = ds.config.schema.get(&label).ok_or_else(|| { + anyhow::anyhow!("schema field {:?} not found in config", self.path) + })?; + Arc::new(spec.path.clone()) + } else { + let path = ObjectPath::from_str(&self.path) + .with_context(|| format!("invalid path {:?}", self.path))?; + Arc::new(vec![path]) + }; + let jobs = self.jobs.max(1); let state = ExtractState { ignore_mime: false }; @@ -81,16 +96,20 @@ impl CliCmd for ListCommand { let item = item.clone(); let source_name = name.to_string(); let key = item.key().to_string(); - let path = path.clone(); + let schema_paths = schema_paths.clone(); let invert = self.invert; let state = state.clone(); join_set.spawn(async move { - let item = PileValue::Item(item); - let value = item.query(&state, &path).await?; - - let is_present = - matches!(value, Some(v) if !matches!(v, PileValue::Null)); + let pv = PileValue::Item(item); + let mut is_present = false; + for path in schema_paths.as_ref() { + let value = pv.query(&state, path).await?; + if matches!(value, Some(v) if !matches!(v, PileValue::Null)) { + is_present = true; + break; + } + } let should_print = if invert { !is_present } else { is_present };