/* * Copyright (c) 2022–2023 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/. */ #![no_main] use std::{ fs::File, io::Read, iter::Peekable, os::fd::{ FromRawFd }, path::Path, str::FromStr, }; use c_main::Args; 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) => { if tabkey.peek().is_some() { root = toml::Value::Table(table.to_owned()); continue; }; }, _ => { if tabkey.peek().is_some() { return Err((format!("{}: Not a table.", item), EX_DATAERR)); } }, }; match value { Value::Array(array) => { // TODO: Split Array logic into separate function 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.", i), 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) => out.push_str(&format!("{:?}", boolean)), Value::Datetime(datetime) => out.push_str(&format!("{:?}", datetime)), Value::Float(float) => out.push_str(&format!("{:?}", float)), Value::Integer(int) => out.push_str(&format!("{:?}", int)), Value::String(string) => out.push_str(string.as_str()), _ => return Err((format!("{:?}: No such key.", item), EX_DATAERR)), }; } Ok(out) } #[no_mangle] fn rust_main(args: Args) { let argv: Vec<&str> = args.into_iter().collect(); let usage_info = format!("Usage: {} [table.]key[[index]] [file...]", argv[0]); if argv.len() <= 1 { eprintln!("{}", usage_info); exit(EX_USAGE); } let input = argv.get(2).unwrap_or(&""); let mut content = Vec::new(); match input.to_owned() { "-" | "" => unsafe { File::from_raw_fd(0) }, _ => { File::open(Path::new(&input)).unwrap_or_else(|_| { eprintln!( "{}: {}: No such file or directory.", argv[0], &input ); exit(EX_UNAVAILABLE); }) }, }.read_to_end(&mut content) .unwrap_or_else(|_| { eprintln!("{}: Could not read input.", argv[0]); exit(EX_OSERR); }); let mut tabkey: Vec<&str> = argv[1].split(".").collect(); let mut indexvec = Vec::new(); let mut index: Option = None; if tabkey.iter().skip(1).peekable().peek().is_some() { indexvec = tabkey[1].split(&['[', ']'][..]).collect(); tabkey[1] = indexvec.remove(0); }; 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 = String::from_utf8(content) .unwrap_or_else(|_| { eprintln!("{}: Input is not valid UTF-8.", argv[0]); exit(EX_DATAERR); }) .parse::() .unwrap_or_else(|_| { 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); }, }, ); }