Function bodies 46 total
main function · rust · L2-L40 (39 LOC)build.rs
fn main() {
// Get git commit hash with dirty flag
let git_hash = Command::new("git")
.args(["describe", "--always", "--dirty"])
.output()
.ok()
.and_then(|o| {
if o.status.success() {
String::from_utf8(o.stdout).ok()
} else {
None
}
})
.map(|s| s.trim().to_string())
.unwrap_or_else(|| "unknown".to_string());
// Get build date (UTC)
let build_date = Command::new("date")
.args(["-u", "+%Y-%m-%d"])
.output()
.ok()
.and_then(|o| {
if o.status.success() {
String::from_utf8(o.stdout).ok()
} else {
None
}
})
.map(|s| s.trim().to_string())
.unwrap_or_else(|| "unknown".to_string());
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
println!("cargo:rustc-env=BUILD_DATE={}", build_date);
// Rebuild if git HEAD changes
new function · rust · L95-L144 (50 LOC)src/dokuwiki.rs
pub fn new(wiki_url: &str, user: &str, verbosity: Verbosity) -> Result<Self> {
let wiki_url = wiki_url.trim_end_matches('/').to_string();
let rpc_url = format!("{}/lib/exe/jsonrpc.php", wiki_url);
let url: url::Url = rpc_url.parse().context("Invalid wiki URL")?;
let load_path = get_cookie_load_path();
let mut has_loaded_cookies = false;
let cookie_store = if let Ok(ref path) = load_path {
if path.exists() {
match load_netscape_cookies(path, &url) {
Ok(store) => {
has_loaded_cookies = true;
store
}
Err(e) => {
eprintln!("Failed to load cookies from {:?}: {}", path, e);
CookieStore::new(None)
}
}
} else {
eprintln!("Cookie file not found: {:?}", path);
CookieStore::new(None)
save_cookies function · rust · L147-L152 (6 LOC)src/dokuwiki.rs
fn save_cookies(&self) -> Result<()> {
let store = self.cookie_store.read().unwrap();
let url: url::Url = self.rpc_url.parse().unwrap();
let domain = url.host_str().unwrap_or("localhost");
save_netscape_cookies(&store, &self.cookie_path, domain)
}get_cookie_header function · rust · L155-L163 (9 LOC)src/dokuwiki.rs
fn get_cookie_header(&self) -> String {
let store = self.cookie_store.read().unwrap();
let url: url::Url = self.rpc_url.parse().unwrap();
store
.get_request_values(&url)
.map(|(name, value)| format!("{}={}", name, value))
.collect::<Vec<_>>()
.join("; ")
}store_cookies function · rust · L166-L174 (9 LOC)src/dokuwiki.rs
fn store_cookies(&self, response: &reqwest::blocking::Response) {
for cookie_header in response.headers().get_all(SET_COOKIE) {
if let Ok(cookie_str) = cookie_header.to_str() {
let url: url::Url = self.rpc_url.parse().unwrap();
let mut store = self.cookie_store.write().unwrap();
let _ = store.parse(cookie_str, &url);
}
}
}call_inner function · rust · L177-L239 (63 LOC)src/dokuwiki.rs
fn call_inner(&mut self, method: &str, params: Value) -> Result<Value> {
let request = JsonRpcRequest {
jsonrpc: "2.0",
method: method.to_string(),
params,
id: self.request_id,
};
self.request_id += 1;
let cookie_header = self.get_cookie_header();
let mut req = self.client.post(&self.rpc_url)
.header(CONTENT_TYPE, "application/json");
if !cookie_header.is_empty() {
req = req.header(COOKIE, cookie_header);
}
let body = serde_json::to_string(&request)?;
let response = req
.body(body)
.send()
.map_err(|e| anyhow!("HTTP request failed: {}", e))?;
self.store_cookies(&response);
let status = response.status();
let body_text = response.text().map_err(|e| anyhow!("Failed to read response body: {}", e))?;
if !status.is_success() {
return Err(anyhow!("HTTP error {}:call function · rust · L242-L255 (14 LOC)src/dokuwiki.rs
pub fn call(&mut self, method: &str, params: Value) -> Result<Value> {
match self.call_inner(method, params.clone()) {
Ok(value) => Ok(value),
Err(e) => {
let err_str = e.to_string();
if err_str.contains("401") || err_str.contains("Unauthorized") || err_str.contains("not logged in") {
self.reauthenticate()?;
self.call_inner(method, params)
} else {
Err(e)
}
}
}
}Repobility — same analyzer, your code, free for public repos · /scan/
reauthenticate function · rust · L258-L272 (15 LOC)src/dokuwiki.rs
fn reauthenticate(&mut self) -> Result<()> {
self.verbosity.info("Session expired, re-authenticating...");
if self.cookie_path.exists() {
let _ = std::fs::remove_file(&self.cookie_path);
}
*self.cookie_store.write().unwrap() = CookieStore::new(None);
let (user, password) = self.get_credentials()?;
self.login(&user, &password)?;
self.user = user;
self.save_cookies()?;
Ok(())
}wiki_host function · rust · L279-L284 (6 LOC)src/dokuwiki.rs
pub fn wiki_host(&self) -> &str {
self.wiki_url
.strip_prefix("https://")
.or_else(|| self.wiki_url.strip_prefix("http://"))
.unwrap_or(&self.wiki_url)
}ensure_authenticated function · rust · L287-L313 (27 LOC)src/dokuwiki.rs
pub fn ensure_authenticated(&mut self) -> Result<()> {
if self.has_cached_session() {
self.verbosity.info(&format!("Using cached session for {}", self.user));
// If cookies were loaded from env var but we're saving to .git/, copy them
if !self.cookie_path.exists() {
let _ = self.save_cookies();
}
} else {
let (user, password) = self.get_credentials()?;
self.login(&user, &password)?;
self.user = user;
self.save_cookies()?;
}
// Check API version
let version = self.get_api_version()?;
if version < MIN_API_VERSION {
return Err(anyhow!(
"DokuWiki API version {} is too old. Minimum required: {}. Please upgrade DokuWiki.",
version,
MIN_API_VERSION
));
}
self.verbosity.debug(&format!("API version: {}", version));
Ok(())
}get_credentials function · rust · L316-L380 (65 LOC)src/dokuwiki.rs
fn get_credentials(&self) -> Result<(String, String)> {
use std::env;
use std::process::{Command, Stdio};
if let Ok(password) = env::var("DOKUWIKI_PASSWORD") {
let user = if self.user.is_empty() {
env::var("DOKUWIKI_USER").unwrap_or_else(|_| "admin".to_string())
} else {
self.user.clone()
};
self.verbosity.info("Using credentials from environment");
return Ok((user, password));
}
let url: url::Url = self.rpc_url.parse()?;
let host = url.host_str().unwrap_or("unknown");
let mut input = format!("protocol=https\nhost={}\n", host);
if !self.user.is_empty() {
input.push_str(&format!("username={}\n", self.user));
}
input.push('\n');
let mut child = Command::new("git")
.args(["credential", "fill"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stdlogin function · rust · L383-L394 (12 LOC)src/dokuwiki.rs
fn login(&mut self, user: &str, password: &str) -> Result<()> {
let result = self.call_inner("core.login", json!({
"user": user,
"pass": password
}))?;
match result.as_bool() {
Some(true) => Ok(()),
Some(false) => Err(anyhow!("Login failed: invalid credentials")),
_ => Err(anyhow!("Unexpected login response: {:?}", result)),
}
}get_all_pages function · rust · L403-L409 (7 LOC)src/dokuwiki.rs
pub fn get_all_pages(&mut self) -> Result<Vec<PageInfo>> {
let result = self.call("dokuwiki.getPagelist", json!({
"ns": "",
"opts": { "depth": 0 }
}))?;
parse_page_list(&result)
}get_page_list function · rust · L412-L418 (7 LOC)src/dokuwiki.rs
pub fn get_page_list(&mut self, namespace: &str) -> Result<Vec<PageInfo>> {
let result = self.call("dokuwiki.getPagelist", json!({
"ns": namespace,
"opts": { "depth": 0 }
}))?;
parse_page_list(&result)
}get_recent_changes function · rust · L421-L449 (29 LOC)src/dokuwiki.rs
pub fn get_recent_changes(&mut self, since: i64) -> Result<Vec<PageVersion>> {
let result = self.call("core.getRecentPageChanges", json!({
"timestamp": since
}))?;
let arr = result.as_array().ok_or_else(|| anyhow!("Expected array"))?;
let mut changes = Vec::new();
for item in arr {
let page_id = item["id"].as_str().unwrap_or_default().to_string();
let version = item["revision"].as_i64().unwrap_or(0);
let author = item["author"].as_str().unwrap_or_default().to_string();
let summary = item["summary"].as_str().unwrap_or_default().to_string();
let revision_type = item["type"].as_str().unwrap_or("E").to_string();
if !page_id.is_empty() {
changes.push(PageVersion {
page_id: Some(page_id),
version,
author,
summary,
_size: 0,
revisRepobility · MCP-ready · https://repobility.com
get_page_versions function · rust · L452-L478 (27 LOC)src/dokuwiki.rs
pub fn get_page_versions(&mut self, page_id: &str) -> Result<Vec<PageVersion>> {
let result = self.call("core.getPageHistory", json!({
"page": page_id
}))?;
let arr = result.as_array().ok_or_else(|| anyhow!("Expected array"))?;
let mut versions = Vec::new();
for item in arr {
let version = item["revision"].as_i64().unwrap_or(0);
let author = item["author"].as_str().unwrap_or_default().to_string();
let summary = item["summary"].as_str().unwrap_or_default().to_string();
let _size = item["sizechange"].as_i64().unwrap_or(0);
let revision_type = item["type"].as_str().unwrap_or("E").to_string();
versions.push(PageVersion {
page_id: None,
version,
author,
summary,
_size,
revision_type,
});
}
Ok(versions)
}get_page_version function · rust · L481-L490 (10 LOC)src/dokuwiki.rs
pub fn get_page_version(&mut self, page_id: &str, version: i64) -> Result<String> {
let result = self.call("core.getPage", json!({
"page": page_id,
"rev": version
}))?;
result.as_str()
.map(|s| s.to_string())
.ok_or_else(|| anyhow!("Expected string from getPage"))
}put_page function · rust · L493-L500 (8 LOC)src/dokuwiki.rs
pub fn put_page(&mut self, page_id: &str, content: &str, summary: &str) -> Result<()> {
self.call("core.savePage", json!({
"page": page_id,
"text": content,
"summary": summary
}))?;
Ok(())
}get_attachments function · rust · L503-L529 (27 LOC)src/dokuwiki.rs
pub fn get_attachments(&mut self, namespace: &str) -> Result<Vec<MediaInfo>> {
let result = self.call("core.listMedia", json!({
"namespace": namespace,
"depth": 0 // 0 = unlimited depth, list all media recursively
}))?;
let arr = result.as_array().ok_or_else(|| anyhow!("Expected array"))?;
let mut media = Vec::new();
for item in arr {
let id = item["id"].as_str().unwrap_or_default().to_string();
let _size = item["size"].as_i64().unwrap_or(0);
let revision = item["rev"].as_i64().unwrap_or(0);
let author = item["user"].as_str().unwrap_or_default().to_string();
if !id.is_empty() {
media.push(MediaInfo {
id,
_size,
revision,
author,
});
}
}
Ok(media)
}get_recent_media_changes function · rust · L532-L557 (26 LOC)src/dokuwiki.rs
pub fn get_recent_media_changes(&mut self, since: i64) -> Result<Vec<MediaInfo>> {
let result = self.call("core.getRecentMediaChanges", json!({
"timestamp": since
}))?;
let arr = result.as_array().ok_or_else(|| anyhow!("Expected array"))?;
let mut media = Vec::new();
for item in arr {
let id = item["id"].as_str().unwrap_or_default().to_string();
let _size = item["size"].as_i64().unwrap_or(0);
let revision = item["revision"].as_i64().unwrap_or(0);
let author = item["author"].as_str().unwrap_or_default().to_string();
if !id.is_empty() {
media.push(MediaInfo {
id,
_size,
revision,
author,
});
}
}
Ok(media)
}get_media_versions function · rust · L560-L583 (24 LOC)src/dokuwiki.rs
pub fn get_media_versions(&mut self, media_id: &str) -> Result<Vec<MediaVersion>> {
let result = self.call("core.getMediaHistory", json!({
"media": media_id
}))?;
let arr = result.as_array().ok_or_else(|| anyhow!("Expected array"))?;
let mut versions = Vec::new();
for item in arr {
let version = item["revision"].as_i64().unwrap_or(0);
let author = item["author"].as_str().unwrap_or_default().to_string();
let summary = item["summary"].as_str().unwrap_or_default().to_string();
let revision_type = item["type"].as_str().unwrap_or("E").to_string();
versions.push(MediaVersion {
version,
author,
summary,
revision_type,
});
}
Ok(versions)
}get_attachment_version function · rust · L586-L597 (12 LOC)src/dokuwiki.rs
pub fn get_attachment_version(&mut self, media_id: &str, version: i64) -> Result<Vec<u8>> {
let result = self.call("core.getMedia", json!({
"media": media_id,
"rev": version
}))?;
let base64_data = result.as_str()
.ok_or_else(|| anyhow!("Expected base64 string from getMedia"))?;
BASE64.decode(base64_data)
.map_err(|e| anyhow!("Failed to decode base64: {}", e))
}put_attachment function · rust · L600-L609 (10 LOC)src/dokuwiki.rs
pub fn put_attachment(&mut self, media_id: &str, data: &[u8], overwrite: bool) -> Result<()> {
let base64_data = BASE64.encode(data);
self.call("core.saveMedia", json!({
"media": media_id,
"base64": base64_data,
"overwrite": overwrite
}))?;
Ok(())
}Repobility analyzer · published findings · https://repobility.com
get_repo_cookie_path function · rust · L619-L631 (13 LOC)src/dokuwiki.rs
fn get_repo_cookie_path() -> Result<PathBuf> {
let output = std::process::Command::new("git")
.args(["rev-parse", "--git-dir"])
.output()
.context("Failed to find .git directory")?;
if !output.status.success() {
return Err(anyhow!("Not in a git repository"));
}
let git_dir = String::from_utf8_lossy(&output.stdout).trim().to_string();
Ok(PathBuf::from(git_dir).join("dokuwiki-cookies.txt"))
}get_cookie_load_path function · rust · L634-L639 (6 LOC)src/dokuwiki.rs
fn get_cookie_load_path() -> Result<PathBuf> {
if let Ok(cookie_path) = std::env::var("DOKUWIKI_COOKIE_FILE") {
return Ok(PathBuf::from(cookie_path));
}
get_repo_cookie_path()
}load_netscape_cookies function · rust · L642-L681 (40 LOC)src/dokuwiki.rs
fn load_netscape_cookies(path: &std::path::Path, url: &url::Url) -> Result<CookieStore> {
use std::io::BufRead;
let file = fs::File::open(path)?;
let reader = std::io::BufReader::new(file);
let mut store = CookieStore::new(None);
for line in reader.lines() {
let line = line?;
let line = line.trim();
// Skip comments and empty lines (but not #HttpOnly_ lines)
if line.is_empty() || (line.starts_with('#') && !line.starts_with("#HttpOnly_")) {
continue;
}
let fields: Vec<&str> = line.split('\t').collect();
if fields.len() < 7 {
continue;
}
let domain = fields[0].trim_start_matches("#HttpOnly_");
let _host_only = fields[1];
let path = fields[2];
let secure = fields[3].eq_ignore_ascii_case("true");
let _expiry = fields[4];
let name = fields[5];
let value = fields[6];
// Build a Set-Cookie header string
let musave_netscape_cookies function · rust · L684-L716 (33 LOC)src/dokuwiki.rs
fn save_netscape_cookies(store: &CookieStore, path: &std::path::Path, domain: &str) -> Result<()> {
use std::io::Write;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let mut file = fs::File::create(path)?;
writeln!(file, "# Netscape HTTP Cookie File")?;
writeln!(file, "# https://curl.se/docs/http-cookies.html")?;
writeln!(file, "# This file was generated by git-remote-dokuwiki")?;
writeln!(file)?;
// Build a URL for this domain to query cookies
let url: url::Url = format!("https://{}/", domain).parse().unwrap();
for cookie in store.matches(&url) {
let http_only_prefix = if cookie.http_only().unwrap_or(false) { "#HttpOnly_" } else { "" };
let domain_str = cookie.domain().unwrap_or(domain);
let host_only = if domain_str.starts_with('.') { "FALSE" } else { "TRUE" };
let path = cookie.path().unwrap_or("/");
let secure = if cookie.secure().unwrap_or(false) { "TRUE" } else {parse_page_list function · rust · L717-L741 (25 LOC)src/dokuwiki.rs
fn parse_page_list(result: &Value) -> Result<Vec<PageInfo>> {
let arr = result.as_array().ok_or_else(|| anyhow!("Expected array"))?;
let mut pages = Vec::new();
for item in arr {
let id = item["id"].as_str().unwrap_or_default().to_string();
let revision = item["rev"].as_i64().unwrap_or(0);
let _last_modified = item["mtime"].as_i64().unwrap_or(0);
let author = item["user"].as_str().unwrap_or_default().to_string();
let _size = item["size"].as_i64().unwrap_or(0);
if !id.is_empty() {
pages.push(PageInfo {
id,
revision,
_last_modified,
author,
_size,
});
}
}
Ok(pages)
}push_error function · rust · L14-L35 (22 LOC)src/fast_export.rs
fn push_error(failed_item: &str, error: Error, pushed: &[String], pending: &[String]) -> Error {
let mut msg = format!("Push failed while trying to {}\nError: {}\n", failed_item, error);
if !pushed.is_empty() {
msg.push_str("\nSuccessfully pushed:\n");
for item in pushed {
msg.push_str(&format!(" - {}\n", item));
}
}
if !pending.is_empty() {
msg.push_str("\nNot yet pushed:\n");
for item in pending {
msg.push_str(&format!(" - {}\n", item));
}
}
msg.push_str("\nThe wiki may be in an inconsistent state. ");
msg.push_str("Fix the issue and push again to complete the update.");
anyhow!(msg)
}get_last_revision_timestamp function · rust · L38-L50 (13 LOC)src/fast_export.rs
fn get_last_revision_timestamp() -> Option<i64> {
let output = Command::new("git")
.args(["config", "dokuwiki.lastRevision"])
.output()
.ok()?;
if !output.status.success() {
return None;
}
let timestamp_str = String::from_utf8_lossy(&output.stdout);
timestamp_str.trim().parse().ok()
}path_to_page_id function · rust · L60-L74 (15 LOC)src/fast_export.rs
fn path_to_page_id(path: &str, namespace: Option<&str>, extension: &str) -> Option<String> {
// Only handle files with the configured extension
let suffix = format!(".{}", extension);
let path = path.strip_suffix(&suffix)?;
// Convert path separators to colons
let page_id = path.replace('/', ":");
// Prepend namespace if specified
if let Some(ns) = namespace {
Some(format!("{}:{}", ns, page_id))
} else {
Some(page_id)
}
}Repobility (the analyzer behind this table) · https://repobility.com
path_to_media_id function · rust · L83-L93 (11 LOC)src/fast_export.rs
fn path_to_media_id(path: &str, namespace: Option<&str>) -> Option<String> {
// Convert path separators to colons
let media_id = path.replace('/', ":");
// Prepend namespace if specified
if let Some(ns) = namespace {
Some(format!("{}:{}", ns, media_id))
} else {
Some(media_id)
}
}page_id_to_path function · rust · L22-L38 (17 LOC)src/fast_import.rs
fn page_id_to_path(page_id: &str, namespace: Option<&str>, extension: &str) -> String {
let mut id = page_id.to_string();
// Strip namespace prefix if present
if let Some(ns) = namespace {
if let Some(stripped) = id.strip_prefix(&format!("{}:", ns)) {
id = stripped.to_string();
}
}
// Convert colons to path separators and add extension
let parts: Vec<&str> = id.split(':').collect();
let mut path = parts.join("/");
path.push('.');
path.push_str(extension);
path
}media_id_to_path function · rust · L41-L54 (14 LOC)src/fast_import.rs
fn media_id_to_path(media_id: &str, namespace: Option<&str>) -> String {
let mut id = media_id.to_string();
// Strip namespace prefix if present
if let Some(ns) = namespace {
if let Some(stripped) = id.strip_prefix(&format!("{}:", ns)) {
id = stripped.to_string();
}
}
// Convert colons to path separators
let parts: Vec<&str> = id.split(':').collect();
parts.join("/")
}main function · rust · L33-L110 (78 LOC)src/main.rs
fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
// Handle --version flag
if args.len() == 2 && (args[1] == "--version" || args[1] == "-V") {
println!("git-remote-dokuwiki {}", version_string());
return Ok(());
}
if args.len() < 3 {
eprintln!("git-remote-dokuwiki {}", version_string());
eprintln!();
eprintln!("Usage: git-remote-dokuwiki <remote-name> <url>");
eprintln!("This is a git remote helper and should be invoked by git.");
eprintln!();
eprintln!("Examples:");
eprintln!(" git clone dokuwiki::[email protected]");
eprintln!(" git clone dokuwiki::[email protected]/namespace");
std::process::exit(1);
}
let _remote_name = &args[1];
let url = &args[2];
let verbosity = Verbosity::from_env();
let mut helper = RemoteHelper::new(url, verbosity)?;
let stdin = io::stdin();
let mut stdin = stdin.lock();
let mut stdonew function · rust · L123-L130 (8 LOC)src/main.rs
fn new(url: &str, verbosity: Verbosity) -> Result<Self> {
let (wiki_url, user, namespace, extension) = parse_url(url)?;
let mut client = DokuWikiClient::new(&wiki_url, &user, verbosity)?;
client.ensure_authenticated()?;
Ok(Self { client, namespace, extension, imported: false, verbosity, depth: None, dry_run: false })
}set_option function · rust · L140-L168 (29 LOC)src/main.rs
fn set_option(&mut self, name: &str, value: &str, out: &mut impl Write) -> Result<()> {
match name {
"verbosity" => {
// git sends: no flag=1, -v=2, -vv=3
if let Ok(level) = value.parse::<u8>() {
self.verbosity.set_level(level);
}
writeln!(out, "ok")?;
}
"depth" => {
if let Ok(d) = value.parse::<u32>() {
self.depth = Some(d);
}
writeln!(out, "ok")?;
}
"dry-run" => {
if value == "true" {
self.dry_run = true;
}
writeln!(out, "ok")?;
}
_ => {
// Unsupported option
writeln!(out, "unsupported")?;
}
}
Ok(())
}list function · rust · L169-L196 (28 LOC)src/main.rs
fn list<W: Write>(&mut self, out: &mut W) -> Result<()> {
// DokuWiki doesn't have branches, we simulate a single 'main' branch
// Check if there are new changes on the wiki since our last fetch
let has_new_changes = if let Some(since) = self.get_latest_commit_timestamp() {
// Check for changes newer than our last known revision
self.client.get_recent_changes(since + 1)
.map(|changes| !changes.is_empty())
.unwrap_or(false)
} else {
true // No previous fetch, need to import
};
if has_new_changes {
// Return ? to force git to call import
writeln!(out, "@refs/heads/main HEAD")?;
writeln!(out, "? refs/heads/main")?;
} else if let Some(sha) = self.get_main_sha() {
// No changes, return the current SHA
writeln!(out, "{} refs/heads/main", sha)?;
} else {
// No SHA yet, need to import
import function · rust · L197-L218 (22 LOC)src/main.rs
fn import<W: Write>(&mut self, _ref_name: &str, out: &mut W) -> Result<()> {
if self.imported {
return Ok(());
}
// Check if we already have commits - get the latest timestamp and SHA
let since_timestamp = self.get_latest_commit_timestamp();
let parent_sha = self.get_main_sha();
let wiki_host = self.client.wiki_host().to_string();
let latest_revision = fast_import::generate(&mut self.client, self.namespace.as_deref(), since_timestamp, parent_sha.as_deref(), &wiki_host, &self.extension, self.depth, self.verbosity, out)?;
// Store the latest revision timestamp for future incremental fetches
if let Some(ts) = latest_revision {
self.set_latest_revision_timestamp(ts);
}
self.imported = true;
// Note: 'done' is written after all import commands are processed
Ok(())
}Repobility — same analyzer, your code, free for public repos · /scan/
get_latest_commit_timestamp function · rust · L222-L234 (13 LOC)src/main.rs
fn get_latest_commit_timestamp(&self) -> Option<i64> {
let output = ProcessCommand::new("git")
.args(["config", "dokuwiki.lastRevision"])
.output()
.ok()?;
if !output.status.success() {
return None;
}
let timestamp_str = String::from_utf8_lossy(&output.stdout);
timestamp_str.trim().parse().ok()
}get_main_sha function · rust · L244-L255 (12 LOC)src/main.rs
fn get_main_sha(&self) -> Option<String> {
let output = ProcessCommand::new("git")
.args(["rev-parse", "refs/dokuwiki/origin/heads/main"])
.output()
.ok()?;
if !output.status.success() {
return None;
}
Some(String::from_utf8_lossy(&output.stdout).trim().to_string())
}export function · rust · L256-L301 (46 LOC)src/main.rs
fn export<W: Write, R: io::BufRead>(
&mut self,
out: &mut W,
reader: &mut R,
) -> Result<()> {
self.verbosity.info("Exporting to wiki...");
// Check if remote has changes we haven't fetched
// This prevents overwriting remote changes (non-fast-forward)
if let Some(since) = self.get_latest_commit_timestamp() {
let has_remote_changes = self.client.get_recent_changes(since + 1)
.map(|changes| !changes.is_empty())
.unwrap_or(false);
if has_remote_changes {
eprintln!("error: failed to push some refs");
eprintln!("hint: Updates were rejected because the remote contains work that you do not");
eprintln!("hint: have locally. This is usually caused by another repository pushing to");
eprintln!("hint: the same ref. If you want to integrate the remote changes, use");
eprintln!("hint: 'git pull' beparse_command function · rust · L23-L48 (26 LOC)src/protocol.rs
pub fn parse_command(line: &str) -> Command {
let line = line.trim();
if line.is_empty() {
return Command::Empty;
}
let mut parts = line.splitn(2, ' ');
let cmd = parts.next().unwrap_or("");
let arg = parts.next().unwrap_or("").to_string();
match cmd {
"capabilities" => Command::Capabilities,
"list" => Command::List,
"import" => Command::Import(arg),
"export" => Command::Export,
"option" => {
// Parse "option name value"
let mut option_parts = arg.splitn(2, ' ');
let name = option_parts.next().unwrap_or("").to_string();
let value = option_parts.next().unwrap_or("").to_string();
Command::Option { name, value }
}
_ => Command::Unknown(line.to_string()),
}
}from_env function · rust · L20-L34 (15 LOC)src/verbosity.rs
pub fn from_env() -> Self {
let mut level = 0u8;
// Check tool-specific env var
if let Ok(v) = env::var("DOKUWIKI_VERBOSE") {
if let Ok(l) = v.parse::<u8>() {
// Map DOKUWIKI_VERBOSE=1 to git's verbosity=2 (-v)
// Map DOKUWIKI_VERBOSE=2 to git's verbosity=3 (-vv)
level = l + 1;
}
}
VERBOSITY_LEVEL.store(level, Ordering::SeqCst);
Verbosity
}set_level function · rust · L37-L43 (7 LOC)src/verbosity.rs
pub fn set_level(&self, level: u8) {
// Only increase verbosity, don't decrease (env var takes precedence)
let current = VERBOSITY_LEVEL.load(Ordering::SeqCst);
if level > current {
VERBOSITY_LEVEL.store(level, Ordering::SeqCst);
}
}progress function · rust · L65-L75 (11 LOC)src/verbosity.rs
pub fn progress(&self, msg: &str, current: usize, total: usize, min_level: u8, max_level: u8) {
let level = self.level();
if level >= min_level && level <= max_level {
eprint!("\r{} {}/{} ", msg, current, total);
if current == total {
eprintln!();
} else {
let _ = io::stderr().flush();
}
}
}