hugoの予約投稿をtauriで作る2

こんにちは、ナナオです。 前回の続きです。 hugoを予約投稿するための超ミニマムなCMSを作った hugoの予約投稿アプリをtauriで作る windows側で起動できるようにする 前回は画面表示はできたけど表示がおかしい感じになっていました。 おそらくWSLで起動したことが原因なので、修正します。 windows側のディレクトリに移動して、powershellで再度tauri-cliをインストールします。 cargo install tauri-cli 再度起動してみます。 cargo tauri dev 今度はちゃんと表示されました。 hugoディレクトリの監視とデプロイを行うようにする 今の状態だと予約投稿する記事の管理はGUIでできるようになりましたが、予約投稿自体ができていません。 ということで少し実装を修正します。 lib.rsを以下のように修正します。 use chrono::{Local, NaiveDateTime}; use regex::Regex; use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use std::thread; use std::time::Duration; #[derive(Debug, Serialize, Deserialize, Clone)] struct ScheduledPost { date: String, file: String, } #[derive(Debug, Serialize, Deserialize, Clone)] struct Config { workdir: String, scheduled: Vec<ScheduledPost>, } const CONFIG_PATH: &str = "../config.yaml"; // パスは適宜調整 #[tauri::command] fn get_config() -> Result<Config, String> { let content = fs::read_to_string(CONFIG_PATH).map_err(|e| e.to_string())?; let config: Config = serde_yaml::from_str(&content).map_err(|e| e.to_string())?; Ok(config) } #[tauri::command] fn save_config(config: Config) -> Result<(), String> { let yaml = serde_yaml::to_string(&config).map_err(|e| e.to_string())?; fs::write(CONFIG_PATH, yaml).map_err(|e| e.to_string())?; Ok(()) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .setup(|app| { if cfg!(debug_assertions) { app.handle().plugin( tauri_plugin_log::Builder::default() .level(log::LevelFilter::Info) .build(), )?; } // スレッドを立てて監視を開始 thread::spawn(|| { start_monitoring(); }); Ok(()) }) .invoke_handler(tauri::generate_handler![get_config, save_config]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } fn start_monitoring() { println!("Hugo Local CMS: 予約投稿監視を開始しました..."); // 処理済みファイルを記録するSet (日付+ファイル名 をキーにする) let mut processed: HashSet<String> = HashSet::new(); loop { println!("{} ファイルチェック開始", Local::now().to_rfc3339()); // 設定ファイルの読み込み let config = match load_config(CONFIG_PATH) { Ok(cfg) => cfg, Err(e) => { eprintln!("設定読み込みエラー: {}", e); thread::sleep(Duration::from_secs(10)); continue; } }; let now = chrono::Local::now().naive_local(); for post in config.scheduled { let key = format!("{}{}", post.date, post.file); // 既に処理済みならスキップ if processed.contains(&key) { continue; } // 日付パース (フォーマット: 2026/01/20 18:45:00) let pub_date = match NaiveDateTime::parse_from_str(&post.date, "%Y/%m/%d %H:%M:%S") { Ok(dt) => dt, Err(e) => { eprintln!("日付パースエラー ({}): {}", post.file, e); continue; } }; // 時間チェック if now > pub_date { println!("⏰ 予約時刻になりました: {}", post.file); // publish処理を実行 publish(&config.workdir, &post.file, &post.date); // 処理済みリストに追加 processed.insert(key); } } println!("{} ファイルチェック終了", Local::now().to_rfc3339()); // 30秒待機 thread::sleep(Duration::from_secs(30)); } } fn load_config(path: &str) -> Result<Config, Box<dyn std::error::Error>> { let content = fs::read_to_string(path)?; let config: Config = serde_yaml::from_str(&content)?; Ok(config) } fn publish(work_dir: &str, relative_path: &str, target_date_str: &str) { let full_path = Path::new(work_dir).join(relative_path); // 1. ファイルのドラフト解除 if let Err(e) = modify_draft_status(&full_path, target_date_str) { eprintln!("❌ ファイル修正失敗 [{:?}]: {}", full_path, e); return; } // 2. Hugoコマンド実行 execute_hugo(work_dir); } fn modify_draft_status(path: &PathBuf, target_date_str: &str) -> std::io::Result<()> { // ファイル読み込み let content = fs::read_to_string(path)?; // 書き換えるコンテンツ let mut new_content = content; // 正規表現: 行頭の "draft = true" (スペース許容) を検索 // (?m) はマルチラインモード有効化 let re = Regex::new(r"(?m)^(draft\s*:\s*)true").unwrap(); if re.is_match(&new_content) { // 置換実行 ( ${1} は "draft = " の部分 ) new_content = re.replace(&new_content, "${1}false").to_string(); println!( "📝 Draftフラグを解除しました: {:?}", path.file_name().unwrap() ); } // dateの書き換え // --- 日付のフォーマット変換 --- // config.yaml の "2026/01/20 18:45:00" -> "2026-01-20T18:45:00+09:00" let dt = NaiveDateTime::parse_from_str(target_date_str, "%Y/%m/%d %H:%M:%S").unwrap(); let formatted_date = format!("{}T{}+09:00", dt.date(), dt.time()); let re = Regex::new(r"(?m)^(\s*date\s*:\s*).*").unwrap(); if re.is_match(&new_content) { let replacement = format!("${{1}}{}", formatted_date); new_content = re.replace(&new_content, replacement.as_str()).to_string(); println!("📅 日付を更新しました: {}", formatted_date); } // 書き込み fs::write(path, new_content.as_bytes())?; println!("📝 Frontmatterの更新完了: {:?}", path.file_name().unwrap()); Ok(()) } fn execute_hugo(work_dir: &str) { println!("🏗️ Hugoビルドを開始します..."); // 1. hugo if run_command(work_dir, "hugo", &[]).is_err() { return; } // 2. hugo deploy if run_command(work_dir, "hugo", &["deploy"]).is_err() { return; } println!("✅ Hugoのビルドとデプロイが完了しました。"); } fn run_command(dir: &str, command: &str, args: &[&str]) -> Result<(), ()> { println!("🚀 実行中: {} {:?} (in {})", command, args, dir); let status = Command::new(command) .args(args) .current_dir(dir) // 作業ディレクトリ指定 .status(); // 実行して終了ステータスを待つ match status { Ok(s) if s.success() => Ok(()), Ok(s) => { eprintln!("エラー: コマンドが失敗しました (Exit Code: {:?})", s.code()); Err(()) } Err(e) => { eprintln!("エラー: コマンドを実行できませんでした: {}", e); Err(()) } } } これで既存の機能を移し替えることができました。 ...

2026年2月11日 · にあえん

hugoの予約投稿アプリをtauriで作る

こんにちは、ナナオです。 今回は、前回作ったhugoの予約投稿ツールをtauriでUI作って見やすくしようよ、という記事になります。 Githubはこちら↓ GitHub - satodaiki/hugo-local-cms: hugoの予約投稿管理 tauriのインストールと起動まで まずはプロジェクトにtauriを入れます。 やり方はこちらを参考にします。 プロジェクトの作成 | Tauri フロントエンドのビルドにはviteを利用します。 ❯ npm create vite@latest . > npx > "create-vite" . │ ◇ Current directory is not empty. Please choose how to proceed: │ Ignore files and continue │ ◇ Select a framework: │ React │ ◇ Select a variant: │ TypeScript │ ◇ Use rolldown-vite (Experimental)?: │ No │ ◇ Install with npm and start now? │ Yes │ ◇ Scaffolding project in /home/nanao/Project/personal/hugo-local-cms... │ ◇ Installing dependencies with npm... added 175 packages, and audited 176 packages in 14s 45 packages are looking for funding run `npm fund` for details found 0 vulnerabilities │ ◇ Starting dev server... React + TypeScriptで実装することにしました。 ...

2026年2月5日 · にあえん

tauriを使ってXCodeでiPhoneをエミュレーションする

どうも、やっぱりRustが好きなナナオです。 前回、せっかくHackintosh化に成功したので、XCodeでなんかエミュレートしたいんですよね。 Lenovo ThinkPad X270にHackintoshでSequoiaを入れた | にあえん ということで今回はtauriを使ってみようと思います。 プロジェクトの作成 tauriのプロジェクトの作成は公式ドキュメントに従ってやっていきましょう。 Create a Project | Tauri とりあえずプロジェクト作成に必要なツールをダウンロードしていきましょう。 cargo install create-tauri-app --locked tauri cliというのもインストールしておきましょう。 Command Line Interface | Tauri cargo install tauri-cli --version "^2.0.0" --locked これで準備ができました。 プロジェクトを作成していきます。 cargo create-tauri-app 出来上がったsrc-tauriディレクトリ内で以下のコマンドを実行します。 cargo tauri dev とりあえずデスクトップアプリの起動には成功しました! iPhoneでエミュレートしてみる iOSで起動するには、同ディレクトリ内で以下のコマンドを実行する必要があります。 cargo tauri ios init しかし残念ながらエラーが出てしまいました。。 Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP. Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`). Info package `cocoapods` present: false Installing `cocoapods`... Error: Cask 'cocoapods' is not installed. `sudo` is required to install cocoapods using gem Error failed to install Apple dependencies: Failed to install `cocoapods`: No such file or directory (os error 2): No such file or directory (os error 2) cocoapodsというツールがインストールされていなかったようです。 ...

2024年12月22日 · にあえん