7 Commits

Author SHA1 Message Date
d27bea2c43 Util to get sometimes-empty config property
The get_property function needs to say that there is no property so that
the caller can respond appropriately. I'm going to need to frequently
respond to the "no such property" path by treating it as *not* an error.

If the config file doesn't specify a property, that's not an error, it's
just not specified and the default should be used instead. This util fn
makes that a bit more ergonomic.
2025-07-17 09:59:52 -05:00
30d8bcc6de Util fn's can use anything that impl's ToString
I don't want to remember to construct a `String` every single time I
want to call this function with a string literal. So I won't.

Make the functions generic over anything that implements the ToString
trait.
2025-07-17 09:57:49 -05:00
b2b9c8b9d9 Add a get-table util function 2025-07-16 19:16:43 -05:00
f6bab75644 Add property-get utility function
This function will look for a given property in a given table. It gives
back either the property, or an explanation of why it could not be
retrieved.
2025-07-14 21:43:08 -05:00
075a2ee921 Scaffold the new config module 2025-07-06 17:28:11 -05:00
8eacb510a2 Add a Cargo.toml & Git tag version comparison
I released the first couple versions without updating the value in
Cargo.toml. This will check for that happening again and abort the
build.
2025-07-06 12:54:52 -05:00
a23bdf3e34 Make the README title singular
It's not the "gt-tools" anymore, so maybe the README should match.
2025-07-05 17:21:59 -05:00
5 changed files with 117 additions and 1 deletions

View File

@@ -8,6 +8,11 @@ jobs:
name: Compile and upload a release build
steps:
- uses: actions/checkout@v4
- name: Get Cargo version
run: echo "cargo_version=v$(grep "^version" Cargo.toml | cut -d \" -f2)" >> $GITHUB_ENV
- name: Abort if Cargo.toml & Git Tag versions don't match
if: ${{ env.cargo_version != github.ref_name }}
run: exit 1
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
- name: Build binary crate

View File

@@ -10,6 +10,7 @@ itertools = "0.10.0"
reqwest = { version = "0.11.13", features = ["json", "stream", "multipart"] }
serde = { version = "1.0.152", features = ["derive"] }
tokio = { version = "1.24.2", features = ["macros", "rt-multi-thread"] }
toml = "0.5"
# Packages available in Debian (Sid)
# clap = "4.5.23"

View File

@@ -1,4 +1,4 @@
# gt-tools
# gt-tool
CLI tools for interacting with the Gitea API. Use interactively to talk to your Gitea instance, or automatically via a CI/CD pipeline.

109
src/config.rs Normal file
View File

@@ -0,0 +1,109 @@
use toml::{Value, value::Table};
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub enum Error {
BadFormat,
NoSuchProperty,
NoSuchTable,
TomlWrap(toml::de::Error),
}
impl From<toml::de::Error> for Error {
fn from(value: toml::de::Error) -> Self {
Error::TomlWrap(value)
}
}
impl core::fmt::Display for Error{
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// FIXME: Print a nice output, don't just reuse the Debug impl
write!(fmt, "{self:?}")
}
}
impl std::error::Error for Error {}
/// The outer value must be a Table so we can get the sub-table from it.
fn get_table<'outer>(outer: &'outer Table, table_name: impl ToString) -> Result<&'outer Table> {
Ok(outer
.get(&table_name.to_string())
.ok_or(Error::NoSuchTable)?
.as_table()
.ok_or(Error::BadFormat)?)
}
/// Similar to `get_property()` but maps the "Error::NoSuchProperty" result to
/// Option::None. Some properties aren't specified, and that's okay... sometimes.
fn get_maybe_property<'outer> (outer: &'outer Table, property: impl ToString) -> Result<Option<&'outer String>> {
let maybe_prop = get_property(outer, property);
match maybe_prop {
Ok(value) => Ok(Some(value)),
Err(e) => {
if let Error::NoSuchProperty = e {
return Ok(None);
} else {
return Err(e);
}
}
}
}
/// The config properties are individual strings. This gets the named property,
/// or an error explaining why it couldn't be fetched.
fn get_property<'outer>(outer: &'outer Table, property: impl ToString) -> Result<&'outer String> {
let maybe_prop = outer.get(&property.to_string()).ok_or(Error::NoSuchProperty)?;
if let Value::String(text) = maybe_prop {
Ok(text)
} else {
Err(Error::BadFormat)
}
}
#[cfg(test)]
mod tests {
use toml::map::Map;
use super::*;
#[test]
fn read_single_prop() -> Result<()> {
let fx_input_str = "owner = \"dingus\"";
let fx_value = fx_input_str.parse::<Value>()?;
let fx_value = fx_value.as_table().ok_or(Error::NoSuchTable)?;
let expected = "dingus";
let res = get_property(&fx_value, String::from("owner"))?;
assert_eq!(res, expected);
Ok(())
}
// The property is given the value of empty-string `""`
#[test]
fn read_single_prop_empty_quotes() -> Result<()> {
let fx_input_str = "owner = \"\"";
let fx_value = fx_input_str.parse::<Value>()?;
let fx_value = fx_value.as_table().ok_or(Error::NoSuchTable)?;
let expected = "";
let res = get_property(&fx_value, String::from("owner"))?;
assert_eq!(res, expected);
Ok(())
}
#[test]
fn read_table() -> Result<()> {
let fx_input_str = "[tab]\nwith_a_garbage = \"value\"";
let fx_value = fx_input_str.parse::<Value>()?;
let fx_value = fx_value.as_table().ok_or(Error::BadFormat)?;
let mut expected: Map<String, Value> = Map::new();
expected.insert(String::from("with_a_garbage"), Value::String(String::from("value")));
let res = get_table(&fx_value, String::from("tab"))?;
assert_eq!(res, &expected);
Ok(())
}
}

View File

@@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
pub mod api;
pub mod cli;
pub mod config;
pub mod structs;
#[derive(Debug, Deserialize, Serialize)]