Skip to content

Commit 893454d

Browse files
author
˜Luan
committed
Simplify CLI by removing subcommands - version 0.2.1
1 parent 59f6980 commit 893454d

8 files changed

Lines changed: 97 additions & 83 deletions

File tree

.asimov/module.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,8 @@ provides:
1212
asimov-x-cataloger:
1313
description: "Fetch X list members from X API"
1414
arguments:
15-
- name: "COMMAND"
16-
description: "Subcommand to execute"
17-
required: true
18-
values: ["get-list-members"]
1915
- name: "URL"
20-
description: "X list URL (e.g., https://x.com/i/lists/random_string)"
16+
description: "X list URL (e.g., https://x.com/i/lists/1234567890)"
2117
required: true
2218
options:
2319
- name: "limit"

CHANGES.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,41 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [0.1.0] - 2024-08-21
8+
## [0.2.1] - 2025-08-23
9+
10+
### Changed
11+
12+
- **CLI Simplification**: Removed subcommand structure for cleaner command interface
13+
- **Command Usage**: Changed from `asimov-x-cataloger get-list-members "URL"` to `asimov-x-cataloger "URL"`
14+
- **Documentation**: Updated README.md and module.yaml to reflect new CLI structure
15+
- **Module Configuration**: Updated .asimov/module.yaml to remove subcommand definitions
16+
17+
### Breaking Changes
18+
19+
- **CLI Interface**: The `get-list-members` subcommand has been removed
20+
- **Command Syntax**: URL is now a direct positional argument instead of requiring a subcommand
21+
22+
### Migration
23+
24+
**Old usage:**
25+
```bash
26+
asimov-x-cataloger get-list-members "https://x.com/i/lists/1234567890" --limit 100
27+
```
28+
29+
**New usage:**
30+
```bash
31+
asimov-x-cataloger "https://x.com/i/lists/1234567890" --limit 100
32+
```
33+
34+
## [0.1.0] - 2025-08-21
935

1036
### Changed
1137

1238
- **Code Formatting**: Improved code formatting and code organization
1339
- **Dependencies**: Cleaned up Cargo.toml and removed unused tracing-subscriber
1440
- **Documentation**: Updated README.md and CHANGES.md formatting
1541

16-
## [0.0.1] - 2024-08-21
42+
## [0.0.1] - 2025-08-21
1743

1844
### Added
1945

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[package]
44
name = "asimov-x-module"
5-
version = "0.2.0"
5+
version = "0.2.1"
66
authors = ["ASIMOV Community"]
77
edition = "2024"
88
#rust-version = "1.85"

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ ASIMOV module for importing data from X (Twitter) lists and converting it to JSO
88
## ✨ Features
99

1010
🐦 **Import List Members**: Fetch and convert X list members to JSON-LD
11-
**User Profiles**: Complete user data including metrics and profile information
11+
🔄 **User Profiles**: Complete user data including metrics and profile information
1212
🔄 **JSON-LD Output**: Structured data compatible with KNOW ontology
1313
**Fast & Reliable**: Built with Rust for performance and safety
14-
**Rate Limit Aware**: Respects X API limits and provides clear error handling
14+
📊 **Rate Limit Aware**: Respects X API limits and provides clear error handling
1515
📊 **Flexible Output**: Support for both JSON and JSONL formats
1616
🔢 **Pagination Control**: Limit results with `--limit` option
1717

@@ -36,13 +36,13 @@ cargo install asimov-x-module
3636

3737
```bash
3838
# Import all list members (default JSONL output)
39-
asimov-x-cataloger get-list-members "https://x.com/i/lists/${random_string}"
39+
asimov-x-cataloger "https://x.com/i/lists/1234567890"
4040

4141
# Import first 100 members only
42-
asimov-x-cataloger get-list-members "https://x.com/i/lists/${random_string}" --limit 100
42+
asimov-x-cataloger "https://x.com/i/lists/1234567890" --limit 100
4343

4444
# Output in JSON format instead of JSONL
45-
asimov-x-cataloger get-list-members "https://x.com/i/lists/${random_string}" --limit 100 --output json
45+
asimov-x-cataloger "https://x.com/i/lists/1234567890" --limit 100 --output json
4646
```
4747

4848
### Other Commands

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.2.0
1+
0.2.1

src/cataloger/main.rs

Lines changed: 57 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use asimov_module::SysexitsError::{self, *};
44
use asimov_x_module::api::x::XClient;
55
use asimov_x_module::providers::x::extract_list_id_from_url;
6-
use clap::{Parser, Subcommand};
6+
use clap::Parser;
77
use clientele::StandardOptions;
88
use std::error::Error;
99

@@ -12,22 +12,16 @@ struct Options {
1212
#[clap(flatten)]
1313
flags: StandardOptions,
1414

15-
#[clap(subcommand)]
16-
command: Option<Commands>,
17-
}
18-
19-
#[derive(Debug, Subcommand)]
20-
enum Commands {
21-
#[clap(about = "Catalog X list members from a list URL.")]
22-
GetListMembers {
23-
url: String,
15+
/// X list URL to catalog
16+
url: Option<String>,
2417

25-
#[clap(short, long)]
26-
limit: Option<usize>,
18+
/// Limit the number of members to fetch
19+
#[clap(short, long)]
20+
limit: Option<usize>,
2721

28-
#[clap(short, long, default_value = "jsonl")]
29-
output: String,
30-
},
22+
/// Output format (json or jsonl)
23+
#[clap(short, long, default_value = "jsonl")]
24+
output: String,
3125
}
3226

3327
pub fn main() -> Result<SysexitsError, Box<dyn Error>> {
@@ -45,62 +39,60 @@ pub fn main() -> Result<SysexitsError, Box<dyn Error>> {
4539
return Ok(EX_OK);
4640
}
4741

42+
// Check if URL is provided
43+
let url = options.url.ok_or_else(|| {
44+
eprintln!("Error: URL is required. Use --help for usage information.");
45+
EX_USAGE
46+
})?;
47+
4848
#[cfg(feature = "tracing")]
4949
asimov_module::init_tracing_subscriber(&options.flags).expect("failed to initialize logging");
5050

51-
match options.command {
52-
Some(Commands::GetListMembers { url, limit, output }) => {
53-
let output_format = if output == "json" || output == "jsonl" {
54-
output
51+
let output_format = if options.output == "json" || options.output == "jsonl" {
52+
options.output
53+
} else {
54+
eprintln!(
55+
"Warning: Invalid output format '{}'. Using 'jsonl' instead.",
56+
options.output
57+
);
58+
"jsonl".to_string()
59+
};
60+
61+
let list_id = extract_list_id_from_url(&url)
62+
.ok_or_else(|| anyhow::anyhow!("Invalid X list URL: {}", url))?;
63+
64+
let client = XClient::new()?;
65+
let api_response = client.fetch_list_members(&list_id, options.limit)?;
66+
67+
let filter = asimov_x_module::jq::x_list();
68+
let transformed = filter
69+
.filter_json(serde_json::to_value(api_response)?)
70+
.map_err(|e| anyhow::anyhow!("JQ filter error: {}", e))?;
71+
72+
match output_format.as_str() {
73+
"jsonl" => {
74+
if let Some(members) = transformed["members"]["items"].as_array() {
75+
for member in members {
76+
println!("{}", serde_json::to_string(member)?);
77+
}
78+
}
79+
},
80+
"json" => {
81+
if cfg!(feature = "pretty") {
82+
colored_json::write_colored_json(&transformed, &mut std::io::stdout())?;
83+
println!();
5584
} else {
56-
eprintln!(
57-
"Warning: Invalid output format '{}'. Using 'jsonl' instead.",
58-
output
59-
);
60-
"jsonl".to_string()
61-
};
62-
63-
let list_id = extract_list_id_from_url(&url)
64-
.ok_or_else(|| anyhow::anyhow!("Invalid X list URL: {}", url))?;
65-
66-
let client = XClient::new()?;
67-
let api_response = client.fetch_list_members(&list_id, limit)?;
68-
69-
let filter = asimov_x_module::jq::x_list();
70-
let transformed = filter
71-
.filter_json(serde_json::to_value(api_response)?)
72-
.map_err(|e| anyhow::anyhow!("JQ filter error: {}", e))?;
73-
74-
match output_format.as_str() {
75-
"jsonl" => {
76-
if let Some(members) = transformed["members"]["items"].as_array() {
77-
for member in members {
78-
println!("{}", serde_json::to_string(member)?);
79-
}
80-
}
81-
},
82-
"json" => {
83-
if cfg!(feature = "pretty") {
84-
colored_json::write_colored_json(&transformed, &mut std::io::stdout())?;
85-
println!();
86-
} else {
87-
println!("{}", serde_json::to_string(&transformed)?);
88-
}
89-
},
90-
_ => {
91-
if let Some(members) = transformed["members"]["items"].as_array() {
92-
for member in members {
93-
println!("{}", serde_json::to_string(member)?);
94-
}
95-
}
96-
},
97-
};
85+
println!("{}", serde_json::to_string(&transformed)?);
86+
}
9887
},
99-
None => {
100-
eprintln!("No command specified. Use --help for usage information.");
101-
return Ok(EX_USAGE);
88+
_ => {
89+
if let Some(members) = transformed["members"]["items"].as_array() {
90+
for member in members {
91+
println!("{}", serde_json::to_string(member)?);
92+
}
93+
}
10294
},
103-
}
95+
};
10496

10597
Ok(EX_OK)
10698
}

tests/api_tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use asimov_x_module::{find_provider_for, providers::x::extract_list_id_from_url}
44

55
#[test]
66
fn test_find_provider_for_x_list_url() {
7-
let url = "https://x.com/i/lists/1958261568329924954";
7+
let url = "https://x.com/i/lists/1234567890";
88
let provider = find_provider_for(url);
99
assert!(provider.is_some());
1010
assert_eq!(provider.unwrap().id, "x-list-members");
@@ -19,9 +19,9 @@ fn test_find_provider_for_unsupported_url() {
1919

2020
#[test]
2121
fn test_extract_list_id_from_url() {
22-
let url = "https://x.com/i/lists/1958261568329924954";
22+
let url = "https://x.com/i/lists/1234567890";
2323
let id = extract_list_id_from_url(url);
24-
assert_eq!(id, Some("1958261568329924954".to_string()));
24+
assert_eq!(id, Some("1234567890".to_string()));
2525
}
2626

2727
#[test]

0 commit comments

Comments
 (0)