ghee 0.6.1

That thin layer of data change management over the filesystem
Documentation
use anyhow::Result;
use ghee_lang::{Predicate, Value, Xattr};
use path_absolutize::Absolutize;
use serde::Serialize;
use thiserror::Error;

use crate::{paths::PathBufExt, walk::walk_records};

use std::{collections::BTreeMap, io::Write, path::PathBuf};

#[derive(Serialize)]
struct FileXattrs<'a, 'b> {
    path: String,
    xattrs: BTreeMap<&'a Xattr, &'b Value>,
}

#[derive(Error, Debug)]
pub enum GetErr {
    #[error("Error serializing JSON: {0}")]
    JsonSerializationError(serde_json::Error),
    #[error("IO error writing xattr(s) to stdin: {0}")]
    IoError(std::io::Error),
}

/** Retrieve and print records from the given `paths`.
 *
 * The records can be restricted by providing `where_` predicates.
 */
pub fn get(
    paths: &Vec<PathBuf>,
    fields: &Vec<Xattr>,
    json: bool,
    where_: &Vec<Predicate>,
    recursive: bool,
    all: bool,
    sort: bool,
    visit_empty: bool,
) -> Result<()> {
    for path in paths {
        let abs_path = path.absolutize().unwrap().to_path_buf();
        walk_records(
            &abs_path,
            where_,
            recursive,
            all,
            sort,
            visit_empty,
            &|record| {
                let output_path = record
                    .original_path_abs()
                    .map(|p| p.relative_to_curdir_if_possible())
                    .unwrap_or_default();

                // Fields that will be output
                let projected_fields: Vec<Xattr> = if fields.is_empty() {
                    record.xattr_values.keys().cloned().collect()
                } else {
                    fields.clone()
                };
                if json {
                    let mut xattrs: BTreeMap<&Xattr, &Value> = BTreeMap::new();

                    for field in projected_fields.iter() {
                        let value = &record.xattr_values[field];

                        xattrs.insert(field, value);
                    }

                    if !xattrs.is_empty() {
                        let file_xattrs = FileXattrs {
                            path: output_path.display().to_string(),
                            xattrs,
                        };

                        println!(
                            "{}",
                            serde_json::to_string(&file_xattrs)
                                .map_err(GetErr::JsonSerializationError)?
                        );
                    }
                } else {
                    for field in projected_fields.iter() {
                        if let Some(value) = record.xattr_values.get(field) {
                            print!("{}\t{}\t", output_path.display(), field);

                            {
                                let mut stdout = std::io::stdout();
                                stdout
                                    .write(value.as_bytes().as_slice())
                                    .map_err(GetErr::IoError)?;
                            }
                            println!();
                        }
                    }
                }

                Ok(())
            },
        )?;
    }
    Ok(())
}

#[cfg(test)]
mod test {
    use ghee_lang::Key;

    use crate::{cmd::create, test_support::TempDirAuto};

    use super::get;

    #[test]
    fn test_get_all_after_create() {
        let dir = TempDirAuto::new("ghee-test-get-all");

        let nonexisting = dir.push("nonexisting");

        let key = Key::from_string("a");

        create(&nonexisting, &key, false).unwrap();

        get(
            &vec![nonexisting.clone()],
            &vec![],
            false,
            &vec![],
            true,
            true,
            false,
            true,
        )
        .unwrap();

        assert!(dir.exists());
    }
}