fj/src/api/mod.rs
Stephen Way eb716ee588
fix: -R works on repo positional commands, null-array deserialization, list endpoint normalization
* `fj repo branches`, `repo topics`, `repo edit`, `repo fork`, `repo sync`,
  `repo archive`, `repo unarchive`, `repo mirror-sync` previously took only
  a positional `repo` argument and rejected `-R/--repo`. Now they accept
  both, with `-R` winning when both are given.
* Forgejo returns `null` for `labels`/`assignees` on issues and PRs when
  empty. The Issue/Pull structs hit `expected a sequence` on every issue
  create. Added a `deserialize_null_to_default` helper on the affected
  fields so null is now coerced to an empty Vec.
* `get_page` similarly bailed when a list endpoint returned a bare `null`
  body. Now treats null and empty body as `[]`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 08:41:57 -07:00

50 lines
1 KiB
Rust

pub mod hook;
pub mod issue;
pub(crate) mod serde_util;
pub mod label;
pub mod notification;
pub mod org;
pub mod protect;
pub mod pull;
pub mod release;
pub mod repo;
pub mod search;
pub mod user;
pub mod workflow;
use anyhow::{anyhow, Result};
/// Split an `owner/name` slug. Returns a helpful error if the form is wrong.
pub fn split_repo(repo: &str) -> Result<(&str, &str)> {
repo.split_once('/')
.filter(|(o, n)| !o.is_empty() && !n.is_empty())
.ok_or_else(|| anyhow!("expected '<owner>/<name>', got '{repo}'"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_normal_slug() {
let (o, n) = split_repo("rasterstate/fj").unwrap();
assert_eq!(o, "rasterstate");
assert_eq!(n, "fj");
}
#[test]
fn rejects_missing_slash() {
assert!(split_repo("fj").is_err());
}
#[test]
fn rejects_empty_owner() {
assert!(split_repo("/fj").is_err());
}
#[test]
fn rejects_empty_name() {
assert!(split_repo("rasterstate/").is_err());
}
}