<commit_before>//! Content interface
use std::fmt;
use std::ops;

use data_encoding::BASE64;
use serde::de::{self, Visitor};
use serde::{Deserialize, Serialize};

use crate::repo_commits::CommitDetails;
use crate::utils::{percent_encode, PATH};
use crate::{Future, Github, Stream};

/// Provides access to the content information for a repository
pub struct Content {
    github: Github,
    owner: String,
    repo: String,
}

impl Content {
    #[doc(hidden)]
    pub fn new<O, R>(github: Github, owner: O, repo: R) -> Self
    where
        O: Into<String>,
        R: Into<String>,
    {
        Content {
            github,
            owner: owner.into(),
            repo: repo.into(),
        }
    }

    fn path(&self, location: &str) -> String {
        // Handle files with spaces and other characters that can mess up the
        // final URL.
        let location = percent_encode(location.as_ref(), PATH);
        format!("/repos/{}/{}/contents{}", self.owner, self.repo, location)
    }

    /// Gets the contents of the location. This could be a file, symlink, or
    /// submodule. To list the contents of a directory, use `iter`.
    pub fn get(&self, location: &str) -> Future<Contents> {
        self.github.get(&self.path(location))
    }

    /// Information on a single file.
    ///
    /// GitHub only supports downloading files up to 1 megabyte in size. If you
    /// need to retrieve larger files, the Git Data API must be used instead.
    pub fn file(&self, location: &str) -> Future<File> {
        self.github.get(&self.path(location))
    }

    /// List the root directory.
    pub fn root(&self) -> Stream<DirectoryItem> {
        self.iter("/")
    }

    /// Provides a stream over the directory items in `location`.
    ///
    /// GitHub limits the number of items returned to 1000 for this API. If you
    /// need to retrieve more items, the Git Data API must be used instead.
    pub fn iter(&self, location: &str) -> Stream<DirectoryItem> {
        self.github.get_stream(&self.path(location))
    }

    /// Creates a file at a specific location in a repository.
    /// You DO NOT need to base64 encode the content, we will do it for you.
    pub fn create(&self, location: &str, content: &str, message: &str) -> Future<NewFileResponse> {
        let file = &NewFile {
            content: BASE64.encode(&content.to_string().into_bytes()),
            message: message.to_string(),
            sha: None,
        };
        self.github.put(&self.path(location), json!(file))
    }

    /// Updates a file at a specific location in a repository.
    /// You DO NOT need to base64 encode the content, we will do it for you.
    pub fn update(
        &self,
        location: &str,
        content: &str,
        message: &str,
        sha: &str,
    ) -> Future<NewFileResponse> {
        let file = &NewFile {
            content: BASE64.encode(&content.to_string().into_bytes()),
            message: message.to_string(),
            sha: Some(sha.to_string()),
        };
        self.github.put(&self.path(location), json!(file))
    }
}

/// Contents of a path in a repository.<commit_msg>content: allow for getting content from a specific ref<commit_after>//! Content interface
use std::fmt;
use std::ops;

use data_encoding::BASE64;
use serde::de::{self, Visitor};
use serde::{Deserialize, Serialize};

use crate::repo_commits::CommitDetails;
use crate::utils::{percent_encode, PATH};
use crate::{Future, Github, Stream};

/// Provides access to the content information for a repository
pub struct Content {
    github: Github,
    owner: String,
    repo: String,
}

impl Content {
    #[doc(hidden)]
    pub fn new<O, R>(github: Github, owner: O, repo: R) -> Self
    where
        O: Into<String>,
        R: Into<String>,
    {
        Content {
            github,
            owner: owner.into(),
            repo: repo.into(),
        }
    }

    fn path(&self, location: &str, ref_: &str) -> String {
        // Handle files with spaces and other characters that can mess up the
        // final URL.
        let location = percent_encode(location.as_ref(), PATH);
        let mut path = format!("/repos/{}/{}/contents{}", self.owner, self.repo, location);
        if !ref_.is_empty() {
            path += &format!("?ref={}", ref_);
        }
        path
    }

    /// Gets the contents of the location. This could be a file, symlink, or
    /// submodule. To list the contents of a directory, use `iter`.
    pub fn get(&self, location: &str, ref_: &str) -> Future<Contents> {
        self.github.get(&self.path(location, ref_))
    }

    /// Information on a single file.
    ///
    /// GitHub only supports downloading files up to 1 megabyte in size. If you
    /// need to retrieve larger files, the Git Data API must be used instead.
    pub fn file(&self, location: &str, ref_: &str) -> Future<File> {
        self.github.get(&self.path(location, ref_))
    }

    /// List the root directory.
    pub fn root(&self, ref_: &str) -> Stream<DirectoryItem> {
        self.iter("/", ref_)
    }

    /// Provides a stream over the directory items in `location`.
    ///
    /// GitHub limits the number of items returned to 1000 for this API. If you
    /// need to retrieve more items, the Git Data API must be used instead.
    pub fn iter(&self, location: &str, ref_: &str) -> Stream<DirectoryItem> {
        self.github.get_stream(&self.path(location, ref_))
    }

    /// Creates a file at a specific location in a repository.
    /// You DO NOT need to base64 encode the content, we will do it for you.
    pub fn create(&self, location: &str, content: &str, message: &str) -> Future<NewFileResponse> {
        let file = &NewFile {
            content: BASE64.encode(&content.to_string().into_bytes()),
            message: message.to_string(),
            sha: None,
        };
        self.github.put(&self.path(location, ""), json!(file))
    }

    /// Updates a file at a specific location in a repository.
    /// You DO NOT need to base64 encode the content, we will do it for you.
    pub fn update(
        &self,
        location: &str,
        content: &str,
        message: &str,
        sha: &str,
    ) -> Future<NewFileResponse> {
        let file = &NewFile {
            content: BASE64.encode(&content.to_string().into_bytes()),
            message: message.to_string(),
            sha: Some(sha.to_string()),
        };
        self.github.put(&self.path(location, ""), json!(file))
    }
}

/// Contents of a path in a repository.
{'fraction_non_alphanumeric': 0.13325164650022975, 'fraction_numerical': 0.004594884362076888, 'mean_word_length': 2.3299337072921977, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1946975', 'n_tokens_mistral': 1928, 'n_tokens_neox': 1883, 'n_words': 660}