### Bash Command to Run Simple Example Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Executes a simple example provided by the SDK, requiring WeChat API credentials to be set as environment variables. ```bash # Run the simple example WECHAT_APP_ID=your_id WECHAT_APP_SECRET=your_secret cargo run --example simple ``` -------------------------------- ### YAML Configuration Example Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/fixtures/example.md A sample YAML configuration file demonstrating server settings, including host, port, and a list of features. ```yaml server: host: localhost port: 8080 features: - authentication - logging - monitoring ``` -------------------------------- ### Bash Environment Variables for Examples Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Sets the necessary WeChat App ID and App Secret environment variables required for running the SDK examples. ```bash export WECHAT_APP_ID="your_wechat_app_id" export WECHAT_APP_SECRET="your_wechat_app_secret" ``` -------------------------------- ### Install wechat-pub-rs and Dependencies Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Add wechat-pub-rs and tokio to your Cargo.toml. Set WeChat App ID and Secret as environment variables. Optionally install Mermaid CLI for diagram support. ```toml # Cargo.toml [dependencies] wechat-pub-rs = "0.7" tokio = { version = "1.50", features = ["full"] } ``` ```bash # Environment variables required at runtime export WECHAT_APP_ID="wx1234567890abcdef" export WECHAT_APP_SECRET="your_32_char_app_secret_here" # Optional: for Mermaid diagram support npm install -g @mermaid-js/mermaid-cli ``` -------------------------------- ### Advanced WeChat Article Upload with Options Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Upload a markdown file with custom options, including theme, title, author, cover image, and comments settings. This example also shows how to manage drafts by getting, updating, and listing them. ```rust use wechat_pub_rs::{WeChatClient, UploadOptions, Result}; #[tokio::main] async fn main() -> Result<()> { let client = WeChatClient::new("your_app_id", "your_app_secret").await?; // Upload with custom options let options = UploadOptions::with_theme("lapis") .title("Custom Title") .author("Custom Author") .cover_image("./cover.jpg") .show_cover(true) .comments(true, false) .source_url("https://example.com"); let draft_id = client.upload_with_options("./article.md", options).await?; // Manage drafts let draft_info = client.get_draft(&draft_id).await?; client.update_draft(&draft_id, "./updated_article.md").await?; // List all drafts let drafts = client.list_drafts(0, 10).await?; println!("Created draft: {}", draft_id); Ok(()) } ``` -------------------------------- ### Markdown Frontmatter Example Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Example of markdown file structure including frontmatter for metadata such as title, author, cover image, theme, and code highlighting. ```markdown --- title: "Your Article Title" author: "Author Name" cover: "images/cover.jpg" # Required: Cover image path theme: "lapis" # Optional: Theme name code: "github" # Optional: Code highlighting theme --- # Your Article Content Your markdown content here with images: ![Alt text](images/example.jpg) ## Code Blocks ```rust fn main() { println!("Hello, WeChat!"); } ``` ``` -------------------------------- ### WeChat Rust SDK Project Structure Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md Illustrates the directory and file structure for the wechat-pub-rs project, organizing code by functionality such as client, authentication, upload, markdown processing, theming, and utilities, along with tests, examples, and documentation. ```text wechat-pub-rs/ ├── Cargo.toml ├── README.md ├── src/ │ ├── lib.rs # 公共API入口 │ ├── client.rs # 主要客户端实现 │ ├── config.rs # 配置管理 │ ├── error.rs # 错误类型定义 │ ├── http/ # HTTP客户端模块 │ │ ├── mod.rs │ │ ├── client.rs │ │ └── retry.rs │ ├── auth/ # 认证模块 │ │ ├── mod.rs │ │ └── token_manager.rs │ ├── upload/ # 上传模块 │ │ ├── mod.rs │ │ ├── image_uploader.rs │ │ └── draft_manager.rs │ ├── markdown/ # Markdown处理 │ │ ├── mod.rs │ │ ├── parser.rs │ │ └── image_extractor.rs │ ├── theme/ # 主题系统 │ │ ├── mod.rs │ │ ├── manager.rs │ │ ├── renderer.rs │ │ └── builtin/ # 内置主题 │ │ ├── default.css │ │ ├── github.css │ │ └── template.html │ └── utils/ # 工具函数 │ ├── mod.rs │ ├── cache.rs │ └── file.rs ├── tests/ # 集成测试 │ ├── integration_test.rs │ └── fixtures/ │ ├── sample.md │ └── test_images/ ├── benches/ # 性能测试 │ └── upload_benchmark.rs ├── examples/ # 示例代码 │ ├── simple_upload.rs │ └── advanced_usage.rs └── docs/ # 文档 ├── api.md ├── themes.md └── troubleshooting.md ``` -------------------------------- ### WeChatClient Get Draft Method Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Retrieve information about an existing draft using its media ID. This is useful for checking draft status or content. ```rust pub async fn get_draft(&self, media_id: &str) -> Result ``` -------------------------------- ### Markdown File Format Example Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Markdown files must include a YAML frontmatter block with at least a 'title' and 'cover' field. Other fields like 'author', 'description', 'theme', and 'code' are optional. ```markdown --- title: "Building a Rust WeChat SDK" author: "Tyr Chen" description: "A short summary shown as the article digest (up to 120 chars)" cover: "images/02-cover.png" theme: "lapis" code: "github" --- # Building a Rust WeChat SDK Introduction paragraph… ![Architecture Diagram](images/arch.png) ```rust fn greet(name: &str) { println!("Hello, {name}!"); } ``` ```mermaid graph LR A[Markdown] --> B[wechat-pub-rs] B --> C[WeChat Draft] ``` ``` -------------------------------- ### JavaScript Regex Test for Code Tag Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/fixtures/example.md A JavaScript example demonstrating the use of regular expressions to test for the presence of a 'code' tag within HTML. ```javascript const regex = /([^<]*)/; const html = "
test
"; console.log("Regex test:", regex.test(html)); ``` -------------------------------- ### Rust Token Manager Structure and Get Access Token Logic Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md Defines the structure for managing WeChat access tokens, including caching and refreshing logic. The `get_access_token` method first checks the cache and then refreshes if necessary. ```rust pub struct TokenManager { app_id: String, app_secret: String, token_cache: Arc>>, http_client: reqwest::Client, } struct AccessToken { token: String, expires_at: Instant, } impl TokenManager { pub async fn get_access_token(&self) -> Result { // 检查缓存 if let Some(token) = self.get_cached_token().await { return Ok(token); } // 获取新令牌 self.refresh_token().await } async fn refresh_token(&self) -> Result { // 防止并发刷新 let _guard = self.refresh_lock.lock().await; // 双重检查 if let Some(token) = self.get_cached_token().await { return Ok(token); } // 调用API获取新令牌 // ... } } ``` -------------------------------- ### Go Main Function Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/fixtures/example.md A basic 'Hello, world!' program in Go, demonstrating the entry point of a Go application. ```go func main() { fmt.Println("Hello from Go") } ``` -------------------------------- ### Initialize WeChat Client in Rust Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/fixtures/example.md Demonstrates initializing a WeChat client using Tokio and environment configuration. Ensure necessary configurations are set in the environment. ```rust #[tokio::main] async fn main() -> Result<()> { // This is a comment let config = Config::from_env()?; let client = WeChatClient::new(&config); println!("Client initialized!"); Ok(()) } ``` -------------------------------- ### Python Function Definition Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/fixtures/example.md A simple Python function that prints a greeting. This serves as a basic example of Python syntax. ```python def hello(): print("Hello from Python") ``` -------------------------------- ### Basic Rust Hello World Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/fixtures/example.md A standard 'Hello, world!' program in Rust, demonstrating the basic structure of a Rust executable. ```rust fn main() { println!("Hello, world!"); } ``` -------------------------------- ### Configure Upload Options Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Builds `UploadOptions` for customizing article publishing. Methods consume and return `Self` for chaining. Defaults to the 'default' theme, comments disabled, and cover shown. ```rust use wechat_pub_rs::UploadOptions; // Minimal: just pick a theme let opts1 = UploadOptions::with_theme("purple"); // Full configuration let opts2 = UploadOptions::with_theme("orangeheart") .title("My Article") .author("Jane Doe") .cover_image("assets/cover.jpg") .show_cover(false) // hide cover in article body .comments(true, true) // only fans can comment .source_url("https://blog.example.com/my-article"); // Inspect fields directly assert_eq!(opts2.theme, "orangeheart"); assert_eq!(opts2.title, Some("My Article".to_string())); assert!(opts2.fans_only_comments); assert!(!opts2.show_cover); ``` -------------------------------- ### 上传选项结构体 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 定义了 `UploadOptions` 结构体,用于配置文章上传的各项参数。 ```rust // 配置结构体 #[derive(Debug, Clone)] pub struct UploadOptions { pub theme: String, // 主题名称 pub title: Option, // 文章标题(默认从MD提取) pub author: Option, // 作者名称 pub cover_image: Option, // 封面图片路径 pub show_cover: bool, // 是否显示封面(默认true) pub enable_comments: bool, // 是否开启评论(默认false) pub fans_only_comments: bool, // 是否仅粉丝可评论(默认false) pub source_url: Option, // 原文链接 } ``` -------------------------------- ### TokenManager Operations Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Demonstrates the usage of TokenManager for managing WeChat access tokens, including getting tokens, forcing refreshes, and checking token information. ```APIDOC ## `TokenManager` ### Description Thread-safe, lazily-refreshing access token manager. Normally used internally by `WeChatClient`; exposed as `pub` for advanced use cases such as building custom WeChat API integrations on top of the same token infrastructure. ### Methods #### `get_access_token()` - **Description**: Retrieves the access token. If the token is expired or not yet fetched, it will be refreshed. This operation is thread-safe and prevents concurrent refreshes. - **Returns**: A `Result` containing the access token string on success, or a `WeChatError` on failure. #### `force_refresh()` - **Description**: Forces an immediate refresh of the access token, regardless of its current expiry status. This is useful after receiving specific API errors like `40001`. - **Returns**: A `Result` containing the newly refreshed access token string on success, or a `WeChatError` on failure. #### `get_token_info()` - **Description**: Retrieves information about the current token, including its expiry status and time until expiry. - **Returns**: An `Option` containing `TokenInfo` if a token is cached, otherwise `None`. #### `clear_cache()` - **Description**: Clears the cached access token, forcing a refresh on the next `get_access_token()` call. - **Returns**: None. This operation is asynchronous. ``` -------------------------------- ### Initialize Theme Manager Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md Creates a new ThemeManager instance and loads built-in themes. Use this to manage content rendering templates. ```rust pub struct ThemeManager { templates: HashMap, markdown_renderer: MarkdownRenderer, } struct ThemeTemplate { css: String, html_template: String, code_theme: String, } impl ThemeManager { pub fn new() -> Self { let mut manager = Self { templates: HashMap::new(), markdown_renderer: MarkdownRenderer::new(), }; // 加载内置主题 manager.load_builtin_themes(); manager } pub async fn render(&self, content: &str, theme: &str, metadata: &HashMap) -> Result { let template = self.templates.get(theme) .ok_or_else(|| WeChatError::ThemeNotFound(theme.to_string()))?; // Markdown -> HTML let html_content = self.markdown_renderer.render(content)?; // 应用模板 let mut context = tera::Context::new(); context.insert("content", &html_content); context.insert("metadata", metadata); let result = template.render(&context)?; Ok(result) } } ``` -------------------------------- ### Rust UploadOptions Builder Pattern Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Demonstrates the fluent builder pattern for constructing UploadOptions, allowing for chained method calls to set various parameters. ```rust UploadOptions::with_theme("lapis") .title("Custom Title") .author("Author") .cover_image("cover.jpg") .show_cover(true) .comments(true, false) .source_url("https://example.com") ``` -------------------------------- ### 图片处理函数 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 异步函数 `process_images` 用于并发上传 Markdown 中引用的图片,并返回处理后的图片信息。 ```rust async fn process_images(&self, images: Vec, base_path: &Path) -> Result> { let tasks: Vec<_> = images.into_iter().map(|img| { let client = self.clone(); let base_path = base_path.to_owned(); tokio::spawn(async move { client.upload_single_image(img, &base_path).await }) }).collect(); // 并发上传所有图片 let results = futures::future::try_join_all(tasks).await?; Ok(results.into_iter().collect::, _>>()?) } ``` -------------------------------- ### WeChatClient New Method Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Instantiate a new WeChatClient using your App ID and App Secret. This is the entry point for all SDK operations. ```rust pub async fn new(app_id: impl Into, app_secret: impl Into) -> Result ``` -------------------------------- ### Process Mermaid Diagrams in Content Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Use `MermaidProcessor` to detect and render Mermaid diagrams from fenced code blocks. It replaces the blocks with image references and caches the output images, regenerating them only if the source is newer. Requires `mmdc` (mermaid-cli) to be installed. ```rust use wechat_pub_rs::mermaid::MermaidProcessor; use std::path::Path; #[tokio::main] async fn main() -> wechat_pub_rs::Result<()> { let content = r###"# Architecture ```mermaid graph LR Client --> SDK SDK --> WeChat_API SDK --> Token_Cache ``` ```mermaid sequenceDiagram User->>SDK: upload("article.md") SDK->>WeChat: POST /material/add_material WeChat-->>SDK: media_id SDK->>WeChat: POST /draft/add WeChat-->>SDK: draft media_id SDK-->>User: draft_id ``` Regular text continues here."###; // Detect without processing let charts = MermaidProcessor::detect_mermaid_blocks(content); println!("Found {} Mermaid blocks", charts.len()); // Expected: Found 2 Mermaid blocks // Full processing (requires mmdc installed) let output_dir = std::path::PathBuf::from("/tmp/article_images"); let processor = MermaidProcessor::new(output_dir, "architecture".to_string()); let (modified, image_refs) = processor .process_mermaid_content(content, Path::new("/tmp/article_images")) .await?; println!("Generated {} images", image_refs.len()); // Expected: Generated 2 images // Mermaid blocks replaced with image markdown: // ![Mermaid Chart 1](./images/architecture-1.png) // ![Mermaid Chart 2](./images/architecture-2.png) assert!(!modified.contains("```mermaid")); assert!(modified.contains("![Mermaid Chart 1]")); Ok(()) } ``` -------------------------------- ### WeChatClient Upload Method Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Upload a markdown file to create a draft. The SDK will automatically use frontmatter for metadata and apply default themes. ```rust pub async fn upload(&self, markdown_path: &str) -> Result ``` -------------------------------- ### 底层上传和创建方法 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 提供上传图片和创建图文草稿的底层 API,供高级用户使用。 ```rust // 底层方法(高级用户) pub async fn upload_image(&self, image_path: &str) -> Result; pub async fn create_draft(&self, articles: Vec
) -> Result; ``` -------------------------------- ### WeChatClient Upload With Options Method Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Upload a markdown file with custom upload options. This allows for fine-grained control over the draft's appearance and metadata. ```rust pub async fn upload_with_options(&self, markdown_path: &str, options: UploadOptions) -> Result ``` -------------------------------- ### Add wechat-pub-rs to Cargo.toml Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Add the wechat-pub-rs crate and tokio to your project's dependencies in Cargo.toml. ```toml wechat-pub-rs = "0.2" tokio = { version = "1.0", features = ["full"] } ``` -------------------------------- ### 核心 API - 简单上传 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 使用最简单的方式上传文章,SDK 会自动检测标题、作者等信息。 ```rust wx.upload("./article.md", "default").await?; ``` -------------------------------- ### 核心 API - 带选项上传 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 提供 `UploadOptions` 结构体,允许自定义标题、作者、封面图等发布选项。 ```rust wx.upload_with_options("./article.md", UploadOptions { theme: "github", title: Some("自定义标题".to_string()), author: Some("作者名".to_string()), cover_image: Some("./cover.jpg"), show_cover: true, enable_comments: true, }).await?; ``` -------------------------------- ### Bash Commands for Rust Testing Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Provides commands for running unit tests, including options to display test output and run specific test cases. ```bash # Run unit tests cargo test # Run tests with output cargo test -- --nocapture # Run specific test cargo test test_name ``` -------------------------------- ### Core API Usage Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md Demonstrates the simplest and an options-based way to upload an article using the WeChat Official Account Rust SDK. ```APIDOC ## Core API Usage ### Description Examples of how to use the core `upload` and `upload_with_options` functions. ### Code Examples ```rust // Simplest usage: automatically detects title, author, etc. wx.upload("./article.md", "default").await?; // Usage with options: provides fine-grained control over article properties. wx.upload_with_options("./article.md", UploadOptions { theme: "github".to_string(), title: Some("Custom Title".to_string()), author: Some("Author Name".to_string()), cover_image: Some("./cover.jpg"), show_cover: true, enable_comments: true, }).await?; ``` ``` -------------------------------- ### WeChatClient::new Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Creates a new WeChatClient instance with provided App ID and App Secret. ```APIDOC ## WeChatClient::new ### Description Creates a new WeChatClient instance with provided App ID and App Secret. ### Method `async fn new(app_id: impl Into, app_secret: impl Into) -> Result` ### Parameters - **app_id** (String) - The App ID for your WeChat Official Account. - **app_secret** (String) - The App Secret for your WeChat Official Account. ### Response #### Success Response (Result) - Returns a `WeChatClient` instance on success. ### Request Example ```rust let client = WeChatClient::new("your_app_id", "your_app_secret").await?; ``` ``` -------------------------------- ### WeChatClient::new Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Creates an authenticated WeChatClient. It validates credential format and constructs internal subsystems. No network calls are made at construction time; the first access token is fetched lazily on the first API call. ```APIDOC ## WeChatClient::new — Create authenticated client Validates credential format (App ID must start with `wx` and be 18 chars; App Secret must be 32 hex chars), constructs internal HTTP, token, upload, draft, markdown, and theme subsystems, and returns a ready-to-use client. No network calls are made at construction time; the first access token is fetched lazily on the first API call. ```rust use wechat_pub_rs::{WeChatClient, Result}; #[tokio::main] async fn main() -> Result<()> { let app_id = std::env::var("WECHAT_APP_ID").expect("WECHAT_APP_ID not set"); let app_secret = std::env::var("WECHAT_APP_SECRET").expect("WECHAT_APP_SECRET not set"); let client = WeChatClient::new(app_id, app_secret).await?; // Inspect available themes println!("Themes: {:?}", client.available_themes()); // Check token state (None until first API call) println!("Token cached: {}", client.get_token_info().await.is_some()); Ok(()) } // Expected output: // Themes: ["default", "lapis", "maize", "orangeheart", "phycat", "pie", "purple", "rainbow"] // Token cached: false ``` ``` -------------------------------- ### Create WeChatClient Instance Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Instantiate WeChatClient by providing WeChat App ID and App Secret. This method validates credentials and sets up internal subsystems. No network calls are made during construction. ```rust use wechat_pub_rs::{WeChatClient, Result}; #[tokio::main] async fn main() -> Result<()> { let app_id = std::env::var("WECHAT_APP_ID").expect("WECHAT_APP_ID not set"); let app_secret = std::env::var("WECHAT_APP_SECRET").expect("WECHAT_APP_SECRET not set"); let client = WeChatClient::new(app_id, app_secret).await?; // Inspect available themes println!("Themes: {:?}", client.available_themes()); // Check token state (None until first API call) println!("Token cached: {}", client.get_token_info().await.is_some()); Ok(()) } // Expected output: // Themes: ["default", "lapis", "maize", "orangeheart", "phycat", "pie", "purple", "rainbow"] // Token cached: false ``` -------------------------------- ### 客户端初始化 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 使用 App ID 和 App Secret 初始化 `WeChatClient`。 ```rust use wechat_pub::{WeChatClient, UploadOptions, Result}; // 客户端初始化 let client = WeChatClient::new("app_id", "app_secret").await?; ``` -------------------------------- ### 草稿管理方法 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 提供了获取、更新、删除和列出图文草稿的 API。 ```rust // 草稿管理方法 pub async fn get_draft(&self, media_id: &str) -> Result; pub async fn update_draft(&self, media_id: &str, markdown_path: &str, theme: &str) -> Result<()>; pub async fn delete_draft(&self, media_id: &str) -> Result<()>; pub async fn list_drafts(&self, offset: u32, count: u32) -> Result>; ``` -------------------------------- ### WeChatClient::upload_with_options Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Uploads a markdown file with custom options for title, author, cover image, and visibility settings. ```APIDOC ## WeChatClient::upload_with_options ### Description Uploads a markdown file with custom options for title, author, cover image, and visibility settings. ### Method `async fn upload_with_options(&self, markdown_path: &str, options: UploadOptions) -> Result` ### Parameters - **markdown_path** (&str) - The path to the markdown file to upload. - **options** (UploadOptions) - An `UploadOptions` struct containing custom settings for the draft. ### Request Body (UploadOptions) - **title** (String) - Optional: Custom title for the article. - **author** (String) - Optional: Custom author for the article. - **cover_image** (String) - Optional: Path to the cover image. - **show_cover** (bool) - Optional: Whether to show the cover image on the article list. - **comments** (bool, bool) - Optional: Tuple to control comment settings (e.g., `(true, false)`). - **source_url** (String) - Optional: URL of the original source. ### Response #### Success Response (Result) - Returns the `media_id` of the created draft on success. ### Request Example ```rust let options = UploadOptions::with_theme("lapis") .title("Custom Title") .author("Custom Author") .cover_image("./cover.jpg") .show_cover(true) .comments(true, false) .source_url("https://example.com"); let draft_id = client.upload_with_options("./article.md", options).await?; ``` ``` -------------------------------- ### Basic WeChat Article Upload Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Create a WeChatClient and upload a markdown file. The client requires your WeChat Official Account's App ID and App Secret. ```rust use wechat_pub_rs::{WeChatClient, Result}; #[tokio::main] async fn main() -> Result<()> { // Create client with your WeChat Official Account credentials let client = WeChatClient::new("your_app_id", "your_app_secret").await?; // Upload markdown file (theme and metadata from frontmatter) let draft_id = client.upload("./article.md").await?; println!("Draft created with ID: {}", draft_id); Ok(()) } ``` -------------------------------- ### 内置主题枚举 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 定义了 SDK 内置的几种主题样式,如 Default, Github, Zhihu 等。 ```rust // 内置主题 pub enum BuiltinTheme { Default, // 简洁默认样式 Github, // GitHub风格 Zhihu, // 知乎风格 Juejin, // 掘金风格 Wechat, // 微信原生风格 } ``` -------------------------------- ### Upload Article with Custom Options Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Uploads a Markdown file to WeChat, allowing full customization of metadata and rendering settings via `UploadOptions`. Options take precedence over frontmatter, except for the theme. ```rust use wechat_pub_rs::{WeChatClient, UploadOptions, Result}; #[tokio::main] async fn main() -> Result<()> { let client = WeChatClient::new( std::env::var("WECHAT_APP_ID")?, std::env::var("WECHAT_APP_SECRET")?, ).await?; let options = UploadOptions::with_theme("lapis") .title("Overridden Title") // overrides frontmatter title .author("Custom Author") // overrides frontmatter author .cover_image("./images/hero.png") // overrides frontmatter cover .show_cover(true) // display cover inside article body .comments(true, false) // enable comments, allow non-fans .source_url("https://example.com/original"); let draft_id = client .upload_with_options("fixtures/example.md", options) .await?; println!("Draft: {draft_id}"); Ok(()) } ``` -------------------------------- ### 主题渲染器 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 定义了 `ThemeRenderer` 结构体,封装了模板引擎、CSS 处理和语法高亮功能,用于将 Markdown 内容渲染为 HTML。 ```rust struct ThemeRenderer { template_engine: Tera, css_processor: CssProcessor, syntax_highlighter: SyntaxHighlighter, } impl ThemeRenderer { async fn render(&self, content: &MarkdownContent, theme: &str) -> Result { let html = self.markdown_to_html(&content.content).await?; let styled_html = self.apply_theme(html, theme).await?; Ok(styled_html) } } ``` -------------------------------- ### Theme System Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md Details on the built-in and custom theme support for rendering Markdown content into HTML. ```APIDOC ## Theme System ### Description Information about the available themes for rendering articles and how to support custom themes. ### Built-in Themes ```rust // Predefined themes for common styles. pub enum BuiltinTheme { Default, // Simple default style Github, // GitHub-like style Zhihu, // Zhihu-style Juejin, // Juejin-style Wechat, // WeChat native style } ``` ### Custom Theme Support ```rust // Structure for defining a custom theme. pub struct CustomTheme { pub css: String, // Custom CSS styles. pub template: String, // HTML template for rendering. pub code_theme: String, // Code highlighting theme. } ``` ``` -------------------------------- ### Rust Project Dependencies for WeChat SDK Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md Lists the core dependencies for the WeChat Public Platform Rust SDK, including HTTP client, async runtime, serialization, Markdown processing, templating, error handling, logging, time, and configuration. ```toml [dependencies] # HTTP客户端 reqwest = { version = "0.11", features = ["json", "multipart"] } # 异步运行时 tokio = { version = "1.0", features = ["full"] } futures = "0.3" # 序列化 serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" # Markdown处理 pulldown-cmark = "0.9" pulldown-cmark-to-cmark = "10.0" # 模板引擎 tera = "1.19" # 错误处理 thiserror = "1.0" anyhow = "1.0" # 日志 log = "0.4" env_logger = "0.10" # 时间处理 chrono = { version = "0.4", features = ["serde"] } # 配置管理 config = "0.13" # 文件处理 mime_guess = "2.0" ``` -------------------------------- ### Rust Utility Methods for WeChat Publishing Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Provides signatures for common utility functions like uploading images, retrieving themes, checking theme existence, and fetching token information. ```rust // Upload single image pub async fn upload_image(&self, image_path: &str) -> Result ``` ```rust // Get available themes pub fn available_themes(&self) -> Vec<&String> ``` ```rust // Check if theme exists pub fn has_theme(&self, theme: &str) -> bool ``` ```rust // Get token info for debugging pub async fn get_token_info(&self) -> Option ``` -------------------------------- ### 自定义主题结构体 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 定义了 `CustomTheme` 结构体,允许用户通过 CSS、HTML 模板和代码高亮主题来自定义样式。 ```rust // 自定义主题支持 pub struct CustomTheme { pub css: String, // CSS样式 pub template: String, // HTML模板 pub code_theme: String, // 代码高亮主题 } ``` -------------------------------- ### Rust Unit and Integration Tests for WeChat SDK Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md Defines the structure for unit and integration tests within the Rust SDK, including placeholders for markdown parsing, image upload, theme rendering, and full workflow testing. Also includes a basic structure for performance benchmarking. ```rust // 单元测试 #[cfg(test)] mod tests { use super::*; use tokio_test; #[tokio::test] async fn test_markdown_parsing() { // 测试Markdown解析功能 } #[tokio::test] async fn test_image_upload() { // 测试图片上传功能 } #[tokio::test] async fn test_theme_rendering() { // 测试主题渲染功能 } } // 集成测试 #[cfg(test)] mod integration_tests { #[tokio::test] async fn test_full_upload_workflow() { // 测试完整的上传流程 } } // 性能测试 mod benches { use criterion::{black_box, criterion_group, criterion_main, Criterion}; fn benchmark_upload(c: &mut Criterion) { c.bench_function("upload_large_article", |b| { b.iter(|| { // 性能基准测试 }) }); } } ``` -------------------------------- ### Markdown 解析结果 Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md 定义了 `MarkdownContent` 结构体,用于存储解析后的 Markdown 内容,包括标题、作者、正文、图片引用等。 ```rust struct MarkdownContent { title: Option, // 从 # 或 front-matter 提取 author: Option, // 从 front-matter 提取 content: String, // 正文内容 images: Vec, // 图片引用列表 metadata: HashMap, // 其他元数据 } struct ImageRef { alt_text: String, // 图片alt文本 original_url: String, // 原始URL或路径 position: (usize, usize), // 在文本中的位置 } ``` -------------------------------- ### UploadOptions Builder Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt A builder pattern struct for customizing Markdown file publishing options. Methods allow chaining for setting theme, title, author, cover image, comment settings, and source URL. ```APIDOC ## `UploadOptions` — Upload configuration builder A builder-pattern struct for customising how a Markdown file is published. All methods consume and return `Self`, enabling chaining. `Default` produces the `"default"` theme with comments disabled and cover shown. ```rust use wechat_pub_rs::UploadOptions; // Minimal: just pick a theme let opts1 = UploadOptions::with_theme("purple"); // Full configuration let opts2 = UploadOptions::with_theme("orangeheart") .title("My Article") .author("Jane Doe") .cover_image("assets/cover.jpg") .show_cover(false) // hide cover in article body .comments(true, true) // only fans can comment .source_url("https://blog.example.com/my-article"); // Inspect fields directly assert_eq!(opts2.theme, "orangeheart"); assert_eq!(opts2.title, Some("My Article".to_string())); assert!(opts2.fans_only_comments); assert!(!opts2.show_cover); ``` ``` -------------------------------- ### Rust Error Handling with WeChatError Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Illustrates how to handle various WeChat publishing errors using a match statement, including file not found, theme not found, and network errors. ```rust use wechat_pub_rs::{WeChatError, Result}; match client.upload("article.md").await { Ok(draft_id) => println!("Success: {}", draft_id), Err(WeChatError::FileNotFound { path }) => { eprintln!("File not found: {}", path); } Err(WeChatError::ThemeNotFound { theme }) => { eprintln!("Theme not found: {}", theme); } Err(WeChatError::Network(err)) => { eprintln!("Network error: {}", err); } Err(err) => eprintln!("Other error: {}", err), } ``` -------------------------------- ### Upload Article to WeChat Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt The `upload` method processes a Markdown file, uploads images, renders HTML, and creates or updates a draft article. It handles errors like FileNotFound, ThemeNotFound, and Network issues. ```rust use wechat_pub_rs::{WeChatClient, WeChatError, Result}; #[tokio::main] async fn main() -> Result<()> { let client = WeChatClient::new( std::env::var("WECHAT_APP_ID")?, std::env::var("WECHAT_APP_SECRET")?, ).await?; // article.md must have `cover:` in its frontmatter match client.upload("fixtures/example.md").await { Ok(draft_id) => { println!("Draft created: {draft_id}"); // draft_id is the WeChat media_id, e.g. "s0001234abcd..." } Err(WeChatError::FileNotFound { path }) => eprintln!("Missing file: {path}"), Err(WeChatError::ThemeNotFound { theme }) => eprintln!("Bad theme: {theme}"), Err(WeChatError::Network { message }) => eprintln!("Network: {message}"), Err(e) => eprintln!("Error: {e}"), } Ok(()) } ``` -------------------------------- ### List WeChat Drafts with Pagination Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Fetches drafts from WeChat with support for pagination. `offset` is zero-based and `count` specifies the page size, with a WeChat maximum of 20 per call. ```rust use wechat_pub_rs::{WeChatClient, Result}; #[tokio::main] async fn main() -> Result<()> { let client = WeChatClient::new( std::env::var("WECHAT_APP_ID")?, std::env::var("WECHAT_APP_SECRET")?, ).await?; // Fetch first page of up to 10 drafts let drafts = client.list_drafts(0, 10).await?; println!("Found {} drafts", drafts.len()); for (i, draft) in drafts.iter().enumerate() { if let Some(article) = draft.content.news_item.first() { println!( " {i}. [{}] {} (updated: {})", draft.media_id, article.title, draft.update_time ); } } // Fetch second page let page2 = client.list_drafts(10, 10).await?; println!("Page 2: {} more drafts", page2.len()); Ok(()) } ``` -------------------------------- ### Rust Development Dependencies for WeChat SDK Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/specs/0001-rust-sdk.md Specifies the development dependencies for the WeChat Public Platform Rust SDK, including testing utilities, mocking, and benchmarking tools. ```toml [dev-dependencies] tokio-test = "0.4" mockito = "1.2" criterion = "0.5" proptest = "1.3" ``` -------------------------------- ### Rust UploadOptions Struct for WeChat Articles Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Defines the structure for customizing article uploads, including theme, title, author, cover image, comment settings, and source URL. ```rust pub struct UploadOptions { pub theme: String, // Theme name pub title: Option, // Custom title pub author: Option, // Custom author pub cover_image: Option, // Cover image path pub show_cover: bool, // Show cover in content pub enable_comments: bool, // Enable comments pub fans_only_comments: bool, // Fans only comments pub source_url: Option, // Source URL } ``` -------------------------------- ### Mermaid Flowchart: Lock-in Analysis Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/fixtures/example.md A Mermaid flowchart detailing various types of switching costs and their contribution to vendor lock-in, alongside the value gained from a product. ```mermaid graph TD A[锁定分析] --> B[切换成本] A --> C[获得的价值] B --> D[供应商锁定] B --> E[产品锁定] B --> F[版本锁定] B --> G[数据锁定] B --> H[技能锁定] B --> I[心智锁定] C --> J[开发速度] C --> K[扩展能力] C --> L[运维简化] ``` -------------------------------- ### WeChatClient::upload Source: https://github.com/tyrchen/wechat-pub-rs/blob/master/README.md Uploads a markdown file as an article draft. Metadata and theme are inferred from the markdown's frontmatter. ```APIDOC ## WeChatClient::upload ### Description Uploads a markdown file as an article draft. Metadata and theme are inferred from the markdown's frontmatter. ### Method `async fn upload(&self, markdown_path: &str) -> Result` ### Parameters - **markdown_path** (&str) - The path to the markdown file to upload. ### Response #### Success Response (Result) - Returns the `media_id` of the created draft on success. ### Request Example ```rust let draft_id = client.upload("./article.md").await?; ``` ``` -------------------------------- ### Render Markdown to HTML with Themes Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Utilize `ThemeManager` to convert Markdown text into HTML with inline CSS, suitable for WeChat. It supports various themes and code highlighting styles, and allows adding custom themes. ```rust use wechat_pub_rs::theme::ThemeManager; use std::collections::HashMap; fn main() -> wechat_pub_rs::Result<()> { let manager = ThemeManager::new(); // List available themes let mut themes = manager.available_themes(); themes.sort(); println!("Themes: {:?}", themes); // Render with the lapis theme and GitHub code highlighting let markdown = r###"# Hello WeChat This is **bold** and *italic* text. ```rust fn main() { println!("Hello, WeChat!"); } ``` "###; let mut metadata = HashMap::new(); metadata.insert("title".to_string(), "My Article".to_string()); metadata.insert("author".to_string(), "Tyr Chen".to_string()); let html = manager.render(markdown, "lapis", "github", &metadata)?; // HTML has inline styles, no external CSS dependencies assert!(html.contains("id=\"wepub\"")); assert!(html.contains("style=")); assert!(html.contains(" Result<()> ``` -------------------------------- ### Parse Markdown File with Metadata Source: https://context7.com/tyrchen/wechat-pub-rs/llms.txt Use `MarkdownParser` to parse a Markdown file and extract frontmatter metadata like title, author, and description, as well as image references. It can also generate a summary from the content. ```rust use wechat_pub_rs::markdown::MarkdownParser; use std::path::Path; #[tokio::main] async fn main() -> wechat_pub_rs::Result<()> { let parser = MarkdownParser::new(); // Parse from file let content = parser.parse_file(Path::new("fixtures/example.md")).await?; println!("Title: {:?}", content.title); println!("Author: {:?}", content.author); println!("Description: {:?}", content.description); println!("Cover: {:?}", content.cover); println!("Theme: {:?}", content.theme); println!("Code theme: {:?}", content.code); println!("Images: {}", content.images.len()); // Auto-generated digest (first paragraph, max 120 chars) let digest = content.description .clone() .unwrap_or_else(|| content.get_summary(120)); println!("Digest: {digest}"); // All extra frontmatter keys for (k, v) in &content.metadata { println!(" {k}: {v}"); } // Image references for img in &content.images { println!(" {} ({}) local={}", img.alt_text, img.original_url, img.is_local); } Ok(()) } // Expected output (for fixtures/example.md): // Title: Some("...") // Author: Some("陈小天") // Description: Some("为了这壶醋,我包了这顿饺子(写了几千行 Rust,做了个工具)") // Cover: Some("images/02-cover.png") ```