Skip to content

Commit 6335e4c

Browse files
committed
feat: added rust api documentation
1 parent 70636ef commit 6335e4c

File tree

17 files changed

+848
-2
lines changed

17 files changed

+848
-2
lines changed

astro.config.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ export default defineConfig({
102102
label: 'Core JavaScript API',
103103
link: '2/reference/core/js',
104104
},
105+
{
106+
label: 'Rust API',
107+
link: '2/reference/core/rust',
108+
},
105109
{
106110
label: 'Core Rust API (via Docs.rs)',
107111
// TODO: Is there a way to link this to the latest pre-released version?

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"dev:setup": "pnpm dev:setup:submodules && pnpm dev:setup:tauri && pnpm dev:setup:plugins-workspace",
1212
"dev": "astro dev",
1313
"format": "prettier -w --cache --plugin prettier-plugin-astro .",
14-
"build:reference": "pnpm --filter js-api-generator run build",
14+
"build:reference": "pnpm --filter js-api-generator run build && pnpm --filter rust-api-generator run build",
1515
"build:astro": "astro build",
1616
"build:i18n": "pnpm --filter docs-i18n-tracker run build",
1717
"build": "pnpm dev:setup && pnpm build:reference && pnpm build:astro && pnpm build:i18n",

packages/rust-api-generator/build.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { parseCrate, crateResolver } from './parser';
2+
import { generatePage } from './generator';
3+
import { join, dirname } from 'node:path';
4+
import { fileURLToPath } from 'node:url';
5+
6+
// IMPORTANT: Keep these up to date and correct
7+
const baseUrl = '/2/reference/core/rust';
8+
const rootDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
9+
const docsPath = join(rootDir, 'src', 'content', 'docs', '2', 'reference', 'core', 'rust');
10+
const documentCrates = [
11+
'tauri',
12+
'tauri-build',
13+
'tauri-codegen',
14+
'tauri-macros',
15+
'tauri-runtime',
16+
'tauri-runtime-wry',
17+
'tauri-utils',
18+
];
19+
20+
async function main() {
21+
console.log('Starting');
22+
for (const crate of documentCrates) {
23+
console.log(`Documenting crate: ${crate}`);
24+
const rustdoc = await crateResolver(crate);
25+
if (!rustdoc) {
26+
console.error(`Crate could not be resolved: ${crate}`);
27+
continue;
28+
}
29+
const pages = await parseCrate(rustdoc, `${baseUrl}/${crate}/`, join(docsPath, crate));
30+
for (const page of pages) {
31+
console.log(`Generating page: ${page.path}`);
32+
await generatePage(page);
33+
}
34+
}
35+
}
36+
37+
main();
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import type { Page, PageContent, PageType } from './types';
2+
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
3+
import { join, dirname } from 'node:path';
4+
5+
/**
6+
* Write a single page to disk
7+
*/
8+
export async function generatePage(page: Page) {
9+
mkdirSync(dirname(page.path), { recursive: true });
10+
writeFileSync(page.path, page.content);
11+
}
12+
13+
/**
14+
* Generates content for a page
15+
*
16+
* @param type
17+
* @returns
18+
*/
19+
export async function generateContent(type: PageType, content: PageContent): Promise<string> {
20+
switch (type) {
21+
case 'crate':
22+
return await generateContentModule(content, true);
23+
case 'module':
24+
return await generateContentModule(content);
25+
case 'struct':
26+
return await generateContentStruct(content);
27+
default:
28+
throw Error('Unknown content type');
29+
}
30+
}
31+
32+
function header(title: string): string {
33+
return `---
34+
title: '${title}'
35+
editUrl: false
36+
prev: false
37+
next: false
38+
---
39+
`;
40+
}
41+
42+
function fixDocs(docs: undefined | null | string): string {
43+
if (!docs) return '';
44+
return docs.split('\n')[0];
45+
}
46+
47+
function members(content: PageContent): string {
48+
const output: string[] = [];
49+
const modules = content.members.filter((val) => 'module' in val.item.inner);
50+
if (modules.length > 0) output.push('## Modules\n\n');
51+
for (const member of modules) {
52+
output.push(
53+
`- [${member.item.name}](${content.moduleUrl}${member.path.path
54+
.slice(1)
55+
.join('/')}): ${fixDocs(member.item.docs)}`
56+
);
57+
}
58+
const structs = content.members.filter((val) => 'struct' in val.item.inner);
59+
if (structs.length > 0) output.push('## Structs\n\n');
60+
for (const member of structs) {
61+
output.push(
62+
`- [${member.item.name}](${content.moduleUrl}${member.item.name}): ${fixDocs(
63+
member.item.docs
64+
)}`
65+
);
66+
}
67+
return output.join('\n');
68+
}
69+
70+
/**
71+
* Generates content for either a crate or a module, they are virtually identical
72+
*
73+
* @param isCrate
74+
* @returns
75+
*/
76+
export async function generateContentModule(
77+
content: PageContent,
78+
isCrate: boolean = false
79+
): Promise<string> {
80+
return `${header((isCrate ? 'Crate' : 'Module') + ' ' + content.title)}
81+
82+
${content.description}
83+
${members(content)}`;
84+
}
85+
86+
/**
87+
* Generates content for a struct
88+
*
89+
* @returns
90+
*/
91+
export async function generateContentStruct(content: PageContent): Promise<string> {
92+
return '';
93+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "rust-api-generator",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"build": "tsm ./build.ts"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"tsm": "^2.3.0"
14+
}
15+
}

packages/rust-api-generator/parser.ts

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import type { RustDoc, Page, PageMember, ID } from './types';
2+
import { PageMemberType, PageType } from './types/pages';
3+
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
4+
import { join, dirname } from 'node:path';
5+
import { fileURLToPath } from 'node:url';
6+
import { generateContent } from './generator';
7+
8+
async function parseModules(
9+
rootPath: string,
10+
baseUrl: string,
11+
rustdoc: RustDoc,
12+
isCrate: boolean = false
13+
): Promise<Page[]> {
14+
const pages: Page[] = [];
15+
if (isCrate) {
16+
const item = rustdoc.index[rustdoc.root];
17+
const path = rustdoc.paths[rustdoc.root];
18+
const members: PageMember[] = item.inner.module.items.map((id: ID) => {
19+
const member: PageMember = {
20+
type: PageMemberType.struct,
21+
item: rustdoc.index[id],
22+
path: rustdoc.paths[id],
23+
};
24+
return member;
25+
});
26+
pages.push({
27+
type: PageType.crate,
28+
path: join(rootPath, path.path.slice(1).join('/'), 'index.md'),
29+
content: await generateContent(PageType.crate, {
30+
title: path.path.join('::'),
31+
description: item.docs ?? '',
32+
moduleUrl: baseUrl,
33+
members: members,
34+
}),
35+
});
36+
} else {
37+
for (const id in rustdoc.paths) {
38+
const path = rustdoc.paths[id];
39+
if (path.kind !== 'module' || path.crate_id !== 0 || id === rustdoc.root) continue;
40+
const item = rustdoc.index[id];
41+
const members: PageMember[] = item.inner.module.items.map((id: ID) => {
42+
const member: PageMember = {
43+
type: PageMemberType.struct,
44+
item: rustdoc.index[id],
45+
path: rustdoc.paths[id],
46+
};
47+
return member;
48+
});
49+
pages.push({
50+
type: PageType.module,
51+
path: join(rootPath, path.path.slice(1).join('/'), 'index.md'),
52+
content: await generateContent(PageType.module, {
53+
title: path.path.join('::'),
54+
description: item.docs ?? '',
55+
moduleUrl: baseUrl,
56+
members: members,
57+
}),
58+
});
59+
}
60+
}
61+
return pages;
62+
}
63+
64+
async function parseStructs(rootPath: string, baseUrl: string, rustdoc: RustDoc): Promise<Page[]> {
65+
const pages: Page[] = [];
66+
return pages;
67+
}
68+
69+
/**
70+
* Parses a single JSON file
71+
*/
72+
export async function parseCrate(
73+
rustdoc: RustDoc,
74+
baseUrl: string,
75+
rootPath: string
76+
): Promise<Page[]> {
77+
let pages: Page[] = [];
78+
const crateItem = rustdoc.index[rustdoc.root];
79+
80+
for (const type in PageType) {
81+
switch (type) {
82+
case 'crate':
83+
pages = pages.concat(await parseModules(rootPath, baseUrl, rustdoc, true));
84+
case 'module':
85+
pages = pages.concat(await parseModules(rootPath, baseUrl, rustdoc));
86+
case 'struct':
87+
pages = pages.concat(await parseStructs(rootPath, baseUrl, rustdoc));
88+
}
89+
}
90+
return pages;
91+
}
92+
93+
/**
94+
* Resolves the path to a json file for external crates
95+
* @param crate
96+
*/
97+
export async function crateResolver(crate: string): Promise<RustDoc | null> {
98+
const targetFolder = '../tauri/target';
99+
for (const file of readdirSync(targetFolder)) {
100+
if (file !== crate + '.json') continue;
101+
102+
const rustdoc: RustDoc = JSON.parse(readFileSync(join(targetFolder, file), 'utf-8'));
103+
rustdoc.name = crate;
104+
return rustdoc;
105+
}
106+
return null;
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './rustdoc';
2+
export * from './pages';
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { Item, ItemSummary } from './rustdoc';
2+
3+
/**
4+
* A `Page` to be rendered
5+
*/
6+
export interface Page {
7+
/**
8+
* The type of the page
9+
*/
10+
type: PageType;
11+
/**
12+
* Path to the page .md file
13+
*/
14+
path: string;
15+
/**
16+
* Contents of the page
17+
*/
18+
content: string;
19+
}
20+
21+
/**
22+
* Page content used for templating
23+
*/
24+
export interface PageContent {
25+
/**
26+
* Page title
27+
*/
28+
title: string;
29+
/**
30+
* URL to the page
31+
*/
32+
moduleUrl: string;
33+
/**
34+
* Page description
35+
*/
36+
description: string;
37+
/**
38+
* Member items of this page
39+
*/
40+
members: PageMember[];
41+
}
42+
43+
export interface PageMember {
44+
type: PageMemberType;
45+
item: Item;
46+
path: ItemSummary;
47+
}
48+
49+
/**
50+
* The type of the page
51+
*/
52+
export enum PageMemberType {
53+
'module' = 'module',
54+
'struct' = 'struct',
55+
}
56+
57+
/**
58+
* The type of the page
59+
*/
60+
export enum PageType {
61+
'crate' = 'crate',
62+
'module' = 'module',
63+
'struct' = 'struct',
64+
}

0 commit comments

Comments
 (0)