Skip to content

Commit 656f264

Browse files
committed
fixup! gnd: Extract shared manifest and watch modules to reduce duplication
1 parent c8b79ab commit 656f264

File tree

3 files changed

+73
-83
lines changed

3 files changed

+73
-83
lines changed

gnd/src/commands/codegen.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::codegen::{
2020
TemplateCodeGenerator, TemplateKind, GENERATED_FILE_NOTE,
2121
};
2222
use crate::formatter::try_format_typescript;
23-
use crate::manifest::{load_manifest, resolve_path, Manifest, SubgraphSource, Template};
23+
use crate::manifest::{load_manifest, resolve_path, DataSource, Manifest, Template};
2424
use crate::migrations;
2525
use crate::output::{step, Step};
2626
use crate::services::IpfsClient;
@@ -142,9 +142,13 @@ async fn generate_types(opt: &CodegenOpt) -> Result<()> {
142142
}
143143

144144
// Generate types for subgraph data sources
145-
if !manifest.subgraph_sources.is_empty() {
146-
generate_subgraph_source_types(&manifest.subgraph_sources, &opt.output_dir, &opt.ipfs)
147-
.await?;
145+
let subgraph_sources: Vec<&DataSource> = manifest
146+
.data_sources
147+
.iter()
148+
.filter(|ds| ds.is_subgraph_source())
149+
.collect();
150+
if !subgraph_sources.is_empty() {
151+
generate_subgraph_source_types(&subgraph_sources, &opt.output_dir, &opt.ipfs).await?;
148152
}
149153

150154
step(Step::Done, "Types generated successfully");
@@ -401,7 +405,7 @@ fn generate_template_types(templates: &[Template], output_dir: &Path) -> Result<
401405
/// For each subgraph data source, this fetches the referenced subgraph's schema
402406
/// from IPFS and generates entity types (without store methods).
403407
async fn generate_subgraph_source_types(
404-
subgraph_sources: &[SubgraphSource],
408+
subgraph_sources: &[&DataSource],
405409
output_dir: &Path,
406410
ipfs_url: &str,
407411
) -> Result<()> {
@@ -421,27 +425,24 @@ async fn generate_subgraph_source_types(
421425
}
422426

423427
for source in subgraph_sources {
428+
// source_address is guaranteed to be Some because of is_subgraph_source() filter
429+
let address = source.source_address.as_ref().unwrap();
430+
424431
step(
425432
Step::Load,
426-
&format!(
427-
"Fetch schema for subgraph {} ({})",
428-
source.name, source.address
429-
),
433+
&format!("Fetch schema for subgraph {} ({})", source.name, address),
430434
);
431435

432436
// Fetch schema from IPFS using block_in_place to allow blocking in async context
433-
let schema_str = ipfs_client.fetch_subgraph_schema(&source.address).await?;
437+
let schema_str = ipfs_client.fetch_subgraph_schema(address).await?;
434438

435439
// Parse the schema
436440
let ast: gql::Document<'_, String> = gql::parse_schema(&schema_str)
437441
.map_err(|e| anyhow::anyhow!("Failed to parse subgraph schema: {}", e))?;
438442

439443
step(
440444
Step::Generate,
441-
&format!(
442-
"Generate types for subgraph {} ({})",
443-
source.name, source.address
444-
),
445+
&format!("Generate types for subgraph {} ({})", source.name, address),
445446
);
446447

447448
// Generate entity types WITHOUT store methods (false = no store methods)
@@ -450,7 +451,7 @@ async fn generate_subgraph_source_types(
450451
Err(e) => {
451452
eprintln!(
452453
"Warning: Failed to create schema generator for subgraph {} ({}): {}",
453-
source.name, source.address, e
454+
source.name, address, e
454455
);
455456
continue;
456457
}

gnd/src/manifest.rs

Lines changed: 40 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,19 @@ use crate::output::{step, Step};
1818
///
1919
/// # Data Source Organization
2020
///
21-
/// Data sources from the manifest are stored in two separate fields based
22-
/// on their kind:
23-
/// - `data_sources`: Contract-style sources (ethereum/contract, etc.) that
24-
/// have mappings and ABIs
25-
/// - `subgraph_sources`: Subgraph references (kind: subgraph) that only
26-
/// have a name and IPFS address
27-
///
28-
/// This separation reflects their different processing needs: contract
29-
/// sources need compilation and ABI generation, while subgraph sources only
30-
/// need schema fetching for type generation.
21+
/// All data sources from the manifest are stored in the `data_sources` field,
22+
/// regardless of their kind. Subgraph data sources (kind: subgraph) are
23+
/// identified by `DataSource::is_subgraph_source()` and have the `source_address`
24+
/// field set to the IPFS deployment ID of the referenced subgraph.
3125
#[derive(Debug)]
3226
pub struct Manifest {
3327
pub spec_version: Version,
3428
pub schema: Option<String>,
3529
pub features: Vec<String>,
3630
pub graft: Option<GraftConfig>,
37-
/// Contract-style data sources with mappings and ABIs to compile.
31+
/// All data sources, including both contract-style sources and subgraph sources.
3832
pub data_sources: Vec<DataSource>,
3933
pub templates: Vec<Template>,
40-
/// Subgraph data sources (kind: subgraph) with IPFS addresses.
41-
/// Used by codegen to generate types for referenced subgraphs.
42-
pub subgraph_sources: Vec<SubgraphSource>,
4334
}
4435

4536
/// Graft configuration.
@@ -58,6 +49,15 @@ pub struct DataSource {
5849
pub mapping_file: Option<String>,
5950
pub api_version: Option<Version>,
6051
pub abis: Vec<Abi>,
52+
/// For subgraph data sources: the IPFS deployment ID of the referenced subgraph.
53+
pub source_address: Option<String>,
54+
}
55+
56+
impl DataSource {
57+
/// Returns true if this is a valid subgraph data source (kind: subgraph with address).
58+
pub fn is_subgraph_source(&self) -> bool {
59+
self.kind == "subgraph" && self.source_address.is_some()
60+
}
6161
}
6262

6363
/// A data source template in the manifest.
@@ -71,20 +71,10 @@ pub struct Template {
7171
pub abis: Vec<Abi>,
7272
}
7373

74-
/// A subgraph data source (kind: subgraph).
75-
/// These reference another subgraph by its IPFS deployment ID.
76-
#[derive(Debug)]
77-
pub struct SubgraphSource {
78-
/// The data source name from the manifest
79-
pub name: String,
80-
/// The IPFS hash (deployment ID) of the referenced subgraph
81-
pub address: String,
82-
}
83-
8474
impl Manifest {
85-
/// Returns the total count of all data sources (contract + subgraph).
75+
/// Returns the total count of all data sources.
8676
pub fn total_source_count(&self) -> usize {
87-
self.data_sources.len() + self.subgraph_sources.len()
77+
self.data_sources.len()
8878
}
8979
}
9080

@@ -143,9 +133,8 @@ pub fn load_manifest(path: &Path) -> Result<Manifest> {
143133
Some(GraftConfig { base, block })
144134
});
145135

146-
// Extract data sources and subgraph sources
136+
// Extract data sources (including subgraph sources)
147137
let mut data_sources = Vec::new();
148-
let mut subgraph_sources = Vec::new();
149138

150139
if let Some(ds_array) = value.get("dataSources").and_then(|ds| ds.as_array()) {
151140
for ds in ds_array {
@@ -154,29 +143,8 @@ pub fn load_manifest(path: &Path) -> Result<Manifest> {
154143
.and_then(|k| k.as_str())
155144
.unwrap_or("ethereum/contract");
156145

157-
if kind == "subgraph" {
158-
// Subgraph data source - extract for codegen only
159-
let name = ds
160-
.get("name")
161-
.and_then(|n| n.as_str())
162-
.unwrap_or("unnamed")
163-
.to_string();
164-
165-
if let Some(address) = ds
166-
.get("source")
167-
.and_then(|s| s.get("address"))
168-
.and_then(|a| a.as_str())
169-
{
170-
subgraph_sources.push(SubgraphSource {
171-
name,
172-
address: address.to_string(),
173-
});
174-
}
175-
} else {
176-
// Regular data source (ethereum/contract, etc.)
177-
if let Some(parsed) = parse_data_source(ds, kind) {
178-
data_sources.push(parsed);
179-
}
146+
if let Some(parsed) = parse_data_source(ds, kind) {
147+
data_sources.push(parsed);
180148
}
181149
}
182150
}
@@ -195,7 +163,6 @@ pub fn load_manifest(path: &Path) -> Result<Manifest> {
195163
graft,
196164
data_sources,
197165
templates,
198-
subgraph_sources,
199166
})
200167
}
201168

@@ -214,6 +181,11 @@ fn parse_data_source(ds: &serde_json::Value, kind: &str) -> Option<DataSource> {
214181
.and_then(|v| v.as_str())
215182
.and_then(|v| Version::parse(v).ok());
216183
let abis = parse_abis(ds.get("mapping").and_then(|m| m.get("abis")));
184+
let source_address = ds
185+
.get("source")
186+
.and_then(|s| s.get("address"))
187+
.and_then(|a| a.as_str())
188+
.map(String::from);
217189

218190
Some(DataSource {
219191
name,
@@ -222,6 +194,7 @@ fn parse_data_source(ds: &serde_json::Value, kind: &str) -> Option<DataSource> {
222194
mapping_file,
223195
api_version,
224196
abis,
197+
source_address,
225198
})
226199
}
227200

@@ -462,13 +435,21 @@ dataSources:
462435
fs::write(&manifest_path, manifest_content).unwrap();
463436

464437
let manifest = load_manifest(&manifest_path).unwrap();
465-
// Contract sources go to data_sources, subgraph sources go to subgraph_sources
466-
assert_eq!(manifest.data_sources.len(), 1);
467-
assert_eq!(manifest.data_sources[0].name, "Token");
468-
assert_eq!(manifest.data_sources[0].kind, "ethereum/contract");
469-
assert_eq!(manifest.subgraph_sources.len(), 1);
470-
assert_eq!(manifest.subgraph_sources[0].name, "SourceSubgraph");
471-
assert_eq!(manifest.subgraph_sources[0].address, "QmSourceHash123");
438+
// All data sources go to data_sources
439+
assert_eq!(manifest.data_sources.len(), 2);
440+
// First is the subgraph source
441+
assert_eq!(manifest.data_sources[0].name, "SourceSubgraph");
442+
assert_eq!(manifest.data_sources[0].kind, "subgraph");
443+
assert!(manifest.data_sources[0].is_subgraph_source());
444+
assert_eq!(
445+
manifest.data_sources[0].source_address,
446+
Some("QmSourceHash123".to_string())
447+
);
448+
// Second is the contract source
449+
assert_eq!(manifest.data_sources[1].name, "Token");
450+
assert_eq!(manifest.data_sources[1].kind, "ethereum/contract");
451+
assert!(!manifest.data_sources[1].is_subgraph_source());
452+
assert_eq!(manifest.data_sources[1].source_address, None);
472453
// total_source_count includes both
473454
assert_eq!(manifest.total_source_count(), 2);
474455
}

gnd/src/validation/mod.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,6 @@ pub(crate) fn format_manifest_errors(errors: &[ManifestValidationError]) -> Stri
301301
#[cfg(test)]
302302
mod tests {
303303
use super::*;
304-
use crate::manifest::SubgraphSource;
305304
use std::fs;
306305
use tempfile::TempDir;
307306

@@ -411,7 +410,6 @@ type Post @entity {
411410
graft: None,
412411
data_sources,
413412
templates,
414-
subgraph_sources: vec![],
415413
}
416414
}
417415

@@ -427,6 +425,19 @@ type Post @entity {
427425
mapping_file: Some(format!("src/{}.ts", name)),
428426
api_version,
429427
abis: vec![],
428+
source_address: None,
429+
}
430+
}
431+
432+
fn create_subgraph_data_source(name: &str, address: &str) -> DataSource {
433+
DataSource {
434+
name: name.to_string(),
435+
kind: "subgraph".to_string(),
436+
network: None,
437+
mapping_file: None,
438+
api_version: None,
439+
abis: vec![],
440+
source_address: Some(address.to_string()),
430441
}
431442
}
432443

@@ -455,20 +466,17 @@ type Post @entity {
455466
.unwrap();
456467

457468
// Manifest with only subgraph sources (no regular data sources)
458-
// TODO: We now store subgraph sources twice
459-
let subgraph_sources = vec![SubgraphSource {
460-
name: "SourceSubgraph".to_string(),
461-
address: "QmSourceHash123".to_string(),
462-
}];
463-
let data_sources = vec![create_data_source("SourceSubgraph", Some("mainnet"), None)];
469+
let data_sources = vec![create_subgraph_data_source(
470+
"SourceSubgraph",
471+
"QmSourceHash123",
472+
)];
464473
let manifest = Manifest {
465474
spec_version: Version::new(1, 0, 0),
466475
schema: Some("schema.graphql".to_string()),
467476
features: vec![],
468477
graft: None,
469478
data_sources,
470479
templates: vec![],
471-
subgraph_sources,
472480
};
473481

474482
let errors = validate_manifest(&manifest, temp_dir.path());

0 commit comments

Comments
 (0)