/* * Copyright (c) 2022 Emma Tebibyte * SPDX-License-Identifier: AGPL-3.0-or-later * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see https://www.gnu.org/licenses/. */ use std::{ env, fs::File, io::{ Read, stdin, }, iter::Peekable, path::Path, str::FromStr, }; use toml::Value; use yacexits::*; fn parse_toml( mut root: Value, mut tabkey: Peekable>, index: Option, ) -> Result { let mut out = String::new(); while let Some(item) = tabkey.next() { let value = match root.get(item) { Some(val) => val, None => { return Err((format!("{}: No such table or key.", item), EX_DATAERR)); }, }; match value { Value::Table(table) => { match tabkey.peek() { Some(_) => { root = toml::Value::Table(table.to_owned()); continue; }, None => {}, // out.push_str(table.as_str()), }; }, _ => { match tabkey.peek() { Some(_) => { return Err((format!("{}: Not a table.", item), EX_DATAERR)); }, None => {}, }; }, }; match value { // TODO: Implement other type parsing Value::Array(array) => { let element: String; match index { Some(i) => { element = match array.get(i) { Some(element) => { match element.as_str() { Some(val) => val.to_owned(), None => { return Err(( format!("{:?}: No value at given key.", index), EX_DATAERR )); }, } }, None => { return Err( (format!("{:?}: No value at given index.", i), EX_DATAERR) ); }, }; }, None => element = format!("{:?}", array), }; out.push_str(&element); }, Value::Boolean(_boolean) => {}, Value::Datetime(_datetime) => {}, Value::Float(_float) => {}, Value::Integer(_int) => {}, Value::String(string) => out.push_str(string.as_str()), _ => return Err((format!("{:?}: No such key.", item), EX_DATAERR)), }; } Ok(out) } fn main() { let argv: Vec = env::args().collect(); if argv.len() <= 1 { eprintln!("Usage: {} [table...].[value[index]] [file...]", argv[0]); exit(64); // sysexits(3) EX_USAGE } let input = match argv.get(2) { Some(val) => val, None => { eprintln!("Usage: {} [table...].[value[index]] [file...]", argv[0]); exit(EX_USAGE); }, }; let mut content = Vec::new(); let file = Path::new(&input); if input == &"-" { match stdin().lock().read_to_end(&mut content) { Ok(_) => {}, Err(_) => { eprintln!("{}: Could not read from standard input.", argv[0]); exit(EX_OSERR); }, }; } else { match File::open(file).unwrap().read_to_end(&mut content) { Ok(_) => {}, Err(_) => { eprintln!("{}: {:?}: No such file or directory.", argv[0], file); exit(EX_UNAVAILABLE); }, }; } let mut tabkey: Vec<&str> = argv[1].split(".").collect(); let mut indexvec = Vec::new(); let mut index: Option = None; match tabkey.iter().skip(1).peekable().peek() { Some(_) => { indexvec = tabkey[1].split(&['[', ']'][..]).collect(); tabkey[1] = indexvec.remove(0); }, None => {}, }; if ! indexvec.is_empty() { let istr = indexvec.remove(0); match usize::from_str(istr) { Ok(i) => index = Some(i), Err(_) => { eprintln!("{}: {}: Cannot index by given value.", argv[0], istr); exit(EX_USAGE); }, }; } let root = match String::from_utf8(content).unwrap().parse::() { Ok(toml) => toml, Err(_) => { eprintln!("{}: Unable to parse TOML.", argv[0]); exit(EX_DATAERR); }, }; let valiter = tabkey.iter().peekable(); println!( "{}", match parse_toml(root, valiter, index) { Ok(val) => val, Err((err, code)) => { eprintln!("{}: {}", argv[0], err); exit(code); }, }, ); }