Skip to content

Commit

Permalink
merge resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
simonhyll committed Aug 16, 2023
1 parent ec24736 commit 9134470
Show file tree
Hide file tree
Showing 17 changed files with 851 additions and 2 deletions.
4 changes: 4 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ export default defineConfig({
label: 'JavaScript API',
link: '2/reference/js',
},
{
label: 'Rust API',
link: '2/reference/core/rust',
},
{
label: 'Rust API (via Docs.rs)',
// TODO: Is there a way to link this to the latest pre-released version?
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dev:setup": "pnpm dev:setup:submodules && pnpm dev:setup:tauri && pnpm dev:setup:plugins-workspace",
"dev": "astro dev",
"format": "prettier -w --cache --plugin prettier-plugin-astro .",
"build:reference": "pnpm --filter js-api-generator run build",
"build:reference": "pnpm --filter js-api-generator run build && pnpm --filter rust-api-generator run build",
"build:astro": "astro build",
"build:i18n": "pnpm --filter docs-i18n-tracker run build",
"build": "pnpm dev:setup && pnpm build:reference && pnpm build:astro && pnpm build:i18n",
Expand Down
37 changes: 37 additions & 0 deletions packages/rust-api-generator/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { parseCrate, crateResolver } from './parser';
import { generatePage } from './generator';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

// IMPORTANT: Keep these up to date and correct
const baseUrl = '/2/reference/core/rust';
const rootDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
const docsPath = join(rootDir, 'src', 'content', 'docs', '2', 'reference', 'core', 'rust');
const documentCrates = [
'tauri',
'tauri-build',
'tauri-codegen',
'tauri-macros',
'tauri-runtime',
'tauri-runtime-wry',
'tauri-utils',
];

async function main() {
console.log('Starting');
for (const crate of documentCrates) {
console.log(`Documenting crate: ${crate}`);
const rustdoc = await crateResolver(crate);
if (!rustdoc) {
console.error(`Crate could not be resolved: ${crate}`);
continue;
}
const pages = await parseCrate(rustdoc, `${baseUrl}/${crate}/`, join(docsPath, crate));
for (const page of pages) {
console.log(`Generating page: ${page.path}`);
await generatePage(page);
}
}
}

main();
93 changes: 93 additions & 0 deletions packages/rust-api-generator/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type { Page, PageContent, PageType } from './types';
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
import { join, dirname } from 'node:path';

/**
* Write a single page to disk
*/
export async function generatePage(page: Page) {
mkdirSync(dirname(page.path), { recursive: true });
writeFileSync(page.path, page.content);
}

/**
* Generates content for a page
*
* @param type
* @returns
*/
export async function generateContent(type: PageType, content: PageContent): Promise<string> {
switch (type) {
case 'crate':
return await generateContentModule(content, true);
case 'module':
return await generateContentModule(content);
case 'struct':
return await generateContentStruct(content);
default:
throw Error('Unknown content type');
}
}

function header(title: string): string {
return `---
title: '${title}'
editUrl: false
prev: false
next: false
---
`;
}

function fixDocs(docs: undefined | null | string): string {
if (!docs) return '';
return docs.split('\n')[0];
}

function members(content: PageContent): string {
const output: string[] = [];
const modules = content.members.filter((val) => 'module' in val.item.inner);
if (modules.length > 0) output.push('## Modules\n\n');
for (const member of modules) {
output.push(
`- [${member.item.name}](${content.moduleUrl}${member.path.path
.slice(1)
.join('/')}): ${fixDocs(member.item.docs)}`
);
}
const structs = content.members.filter((val) => 'struct' in val.item.inner);
if (structs.length > 0) output.push('## Structs\n\n');
for (const member of structs) {
output.push(
`- [${member.item.name}](${content.moduleUrl}${member.item.name}): ${fixDocs(
member.item.docs
)}`
);
}
return output.join('\n');
}

/**
* Generates content for either a crate or a module, they are virtually identical
*
* @param isCrate
* @returns
*/
export async function generateContentModule(
content: PageContent,
isCrate: boolean = false
): Promise<string> {
return `${header((isCrate ? 'Crate' : 'Module') + ' ' + content.title)}
${content.description}
${members(content)}`;
}

/**
* Generates content for a struct
*
* @returns
*/
export async function generateContentStruct(content: PageContent): Promise<string> {
return '';
}
15 changes: 15 additions & 0 deletions packages/rust-api-generator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "rust-api-generator",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "tsm ./build.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"tsm": "^2.3.0"
}
}
107 changes: 107 additions & 0 deletions packages/rust-api-generator/parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import type { RustDoc, Page, PageMember, ID } from './types';
import { PageMemberType, PageType } from './types/pages';
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { generateContent } from './generator';

async function parseModules(
rootPath: string,
baseUrl: string,
rustdoc: RustDoc,
isCrate: boolean = false
): Promise<Page[]> {
const pages: Page[] = [];
if (isCrate) {
const item = rustdoc.index[rustdoc.root];
const path = rustdoc.paths[rustdoc.root];
const members: PageMember[] = item.inner.module.items.map((id: ID) => {
const member: PageMember = {
type: PageMemberType.struct,
item: rustdoc.index[id],
path: rustdoc.paths[id],
};
return member;
});
pages.push({
type: PageType.crate,
path: join(rootPath, path.path.slice(1).join('/'), 'index.md'),
content: await generateContent(PageType.crate, {
title: path.path.join('::'),
description: item.docs ?? '',
moduleUrl: baseUrl,
members: members,
}),
});
} else {
for (const id in rustdoc.paths) {
const path = rustdoc.paths[id];
if (path.kind !== 'module' || path.crate_id !== 0 || id === rustdoc.root) continue;
const item = rustdoc.index[id];
const members: PageMember[] = item.inner.module.items.map((id: ID) => {
const member: PageMember = {
type: PageMemberType.struct,
item: rustdoc.index[id],
path: rustdoc.paths[id],
};
return member;
});
pages.push({
type: PageType.module,
path: join(rootPath, path.path.slice(1).join('/'), 'index.md'),
content: await generateContent(PageType.module, {
title: path.path.join('::'),
description: item.docs ?? '',
moduleUrl: baseUrl,
members: members,
}),
});
}
}
return pages;
}

async function parseStructs(rootPath: string, baseUrl: string, rustdoc: RustDoc): Promise<Page[]> {
const pages: Page[] = [];
return pages;
}

/**
* Parses a single JSON file
*/
export async function parseCrate(
rustdoc: RustDoc,
baseUrl: string,
rootPath: string
): Promise<Page[]> {
let pages: Page[] = [];
const crateItem = rustdoc.index[rustdoc.root];

for (const type in PageType) {
switch (type) {
case 'crate':
pages = pages.concat(await parseModules(rootPath, baseUrl, rustdoc, true));
case 'module':
pages = pages.concat(await parseModules(rootPath, baseUrl, rustdoc));
case 'struct':
pages = pages.concat(await parseStructs(rootPath, baseUrl, rustdoc));
}
}
return pages;
}

/**
* Resolves the path to a json file for external crates
* @param crate
*/
export async function crateResolver(crate: string): Promise<RustDoc | null> {
const targetFolder = '../tauri/target';
for (const file of readdirSync(targetFolder)) {
if (file !== crate + '.json') continue;

const rustdoc: RustDoc = JSON.parse(readFileSync(join(targetFolder, file), 'utf-8'));
rustdoc.name = crate;
return rustdoc;
}
return null;
}
2 changes: 2 additions & 0 deletions packages/rust-api-generator/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './rustdoc';
export * from './pages';
64 changes: 64 additions & 0 deletions packages/rust-api-generator/types/pages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { Item, ItemSummary } from './rustdoc';

/**
* A `Page` to be rendered
*/
export interface Page {
/**
* The type of the page
*/
type: PageType;
/**
* Path to the page .md file
*/
path: string;
/**
* Contents of the page
*/
content: string;
}

/**
* Page content used for templating
*/
export interface PageContent {
/**
* Page title
*/
title: string;
/**
* URL to the page
*/
moduleUrl: string;
/**
* Page description
*/
description: string;
/**
* Member items of this page
*/
members: PageMember[];
}

export interface PageMember {
type: PageMemberType;
item: Item;
path: ItemSummary;
}

/**
* The type of the page
*/
export enum PageMemberType {
'module' = 'module',
'struct' = 'struct',
}

/**
* The type of the page
*/
export enum PageType {
'crate' = 'crate',
'module' = 'module',
'struct' = 'struct',
}
Loading

0 comments on commit 9134470

Please sign in to comment.