├── .all-contributorsrc
├── .github
└── workflows
│ ├── README.md
│ ├── build.yml
│ └── release.yml
├── .gitignore
├── .nvmrc
├── .releaserc.json
├── .yarn
└── releases
│ └── yarn-4.1.0.cjs
├── .yarnrc.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── Setup.hs
├── atomic-write.cabal
├── docs
├── CODE_OF_CONDUCT.md
└── CONTRIBUTING.md
├── examples
└── logger
│ ├── .gitignore
│ ├── README.md
│ ├── Setup.hs
│ ├── app.log
│ ├── app
│ └── Main.hs
│ ├── logger-example.cabal
│ ├── package.yaml
│ ├── stack.yaml
│ └── stack.yaml.lock
├── package.json
├── spec
├── Spec.hs
└── System
│ └── AtomicWrite
│ └── Writer
│ ├── ByteString
│ └── BinarySpec.hs
│ ├── ByteStringBuilderSpec.hs
│ ├── ByteStringSpec.hs
│ ├── LazyByteString
│ └── BinarySpec.hs
│ ├── LazyByteStringSpec.hs
│ ├── LazyText
│ └── BinarySpec.hs
│ ├── LazyTextSpec.hs
│ ├── String
│ └── BinarySpec.hs
│ ├── StringSpec.hs
│ ├── Text
│ └── BinarySpec.hs
│ └── TextSpec.hs
├── src
└── System
│ └── AtomicWrite
│ ├── Internal.hs
│ └── Writer
│ ├── ByteString.hs
│ ├── ByteString
│ └── Binary.hs
│ ├── ByteStringBuilder.hs
│ ├── LazyByteString.hs
│ ├── LazyByteString
│ └── Binary.hs
│ ├── LazyText.hs
│ ├── LazyText
│ └── Binary.hs
│ ├── String.hs
│ ├── String
│ └── Binary.hs
│ ├── Text.hs
│ └── Text
│ └── Binary.hs
├── stack.yaml
├── stack.yaml.lock
└── yarn.lock
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "atomic-write",
3 | "projectOwner": "stackbuilders",
4 | "repoType": "github",
5 | "repoHost": "https://github.com",
6 | "files": [
7 | "README.md"
8 | ],
9 | "imageSize": 100,
10 | "commit": true,
11 | "commitConvention": "gitmoji",
12 | "contributors": [
13 | {
14 | "login": "jsl",
15 | "name": "Justin S. Leitgeb",
16 | "avatar_url": "https://avatars.githubusercontent.com/u/9977?v=4",
17 | "profile": "https://www.stackbuilders.com/news/author/justin-leitgeb",
18 | "contributions": [
19 | "code"
20 | ]
21 | },
22 | {
23 | "login": "cptrodolfox",
24 | "name": "William R. Arellano",
25 | "avatar_url": "https://avatars.githubusercontent.com/u/20303685?v=4",
26 | "profile": "https://github.com/cptrodolfox",
27 | "contributions": [
28 | "code"
29 | ]
30 | },
31 | {
32 | "login": "Alex0jk",
33 | "name": "Alexander Mejía",
34 | "avatar_url": "https://avatars.githubusercontent.com/u/22301755?v=4",
35 | "profile": "https://github.com/Alex0jk",
36 | "contributions": [
37 | "code"
38 | ]
39 | },
40 | {
41 | "login": "acamino",
42 | "name": "Agustin Camino",
43 | "avatar_url": "https://avatars.githubusercontent.com/u/957202?v=4",
44 | "profile": "https://github.com/acamino",
45 | "contributions": [
46 | "code"
47 | ]
48 | },
49 | {
50 | "login": "juanpaucar",
51 | "name": "Juan Paucar",
52 | "avatar_url": "https://avatars.githubusercontent.com/u/2164411?v=4",
53 | "profile": "https://juancarlos.io/",
54 | "contributions": [
55 | "code"
56 | ]
57 | },
58 | {
59 | "login": "BarbDMC",
60 | "name": "Barbara Morantes",
61 | "avatar_url": "https://avatars.githubusercontent.com/u/67979158?v=4",
62 | "profile": "https://www.barbaramorantes.com/",
63 | "contributions": [
64 | "example"
65 | ]
66 | }
67 | ],
68 | "contributorsPerLine": 7,
69 | "linkToUsage": true
70 | }
71 |
--------------------------------------------------------------------------------
/.github/workflows/README.md:
--------------------------------------------------------------------------------
1 | # CI Workflows
2 |
3 | ## Overview
4 |
5 | - [Build](build.yml)
6 | - Build and test Haskell code
7 | - [Draft](draft.yml)
8 | - Create a GH draft release with a static binary
9 | - [Release](release.yml)
10 | - Upload the package and docs to Hackage (release candidate)
11 |
12 | ## Events
13 |
14 | ```mermaid
15 | graph LR;
16 | event[GH Event]-->|on push|Build;
17 | event-->|tag created|Draft;
18 | Draft-->|create draft release|End;
19 | event-->|release published|Release;
20 | Release-->|upload artifacts to Hackage - release candidate|End;
21 | Build-->End;
22 | ```
23 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | workflow_call:
8 |
9 | concurrency:
10 | group: build-${{ github.ref }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | generate-matrix:
15 | name: "Generate matrix from cabal"
16 | outputs:
17 | matrix: ${{ steps.set-matrix.outputs.matrix }}
18 | runs-on: ubuntu-latest
19 | steps:
20 | - name: Extract the tested GHC versions
21 | id: set-matrix
22 | uses: kleidukos/get-tested@v0.1.7.0
23 | with:
24 | cabal-file: atomic-write.cabal
25 | ubuntu-version: latest
26 | version: 0.1.7.0
27 |
28 | build-and-test:
29 | name: GHC ${{ matrix.ghc }} on ${{ matrix.os }}
30 | needs: generate-matrix
31 | strategy:
32 | matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }}
33 | runs-on: ${{ matrix.os }}
34 | steps:
35 | - name: Checkout
36 | uses: actions/checkout@v3
37 | - name: Install Haskell tooling
38 | uses: haskell-actions/setup@v2
39 | with:
40 | ghc-version: ${{ matrix.ghc }}
41 | cabal-version: "3.6"
42 | - name: Configure project
43 | run: cabal configure --enable-tests
44 | - name: Build project
45 | run: cabal build
46 | - name: Run tests
47 | run: cabal test --test-show-details=direct
48 | - name: Check documentation
49 | run: cabal haddock
50 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | concurrency:
7 | group: release
8 | cancel-in-progress: true
9 |
10 | jobs:
11 | build:
12 | uses: ./.github/workflows/build.yml
13 |
14 | upload:
15 | runs-on: ubuntu-latest
16 | needs: [build]
17 | permissions:
18 | contents: write
19 | issues: write
20 | pull-requests: write
21 | id-token: write
22 | steps:
23 | - name: Checkout
24 | uses: actions/checkout@v3
25 | - name: Setup tooling
26 | uses: haskell/actions/setup@v2
27 | with:
28 | ghc-version: "8.10"
29 | cabal-version: "3.6"
30 | - name: Freeze dependencies
31 | run: cabal freeze
32 | - name: Cache dependencies
33 | uses: actions/cache@v3
34 | with:
35 | key: ${{ runner.os }}-${{ hashFiles('*.cabal', 'cabal.project.freeze') }}
36 | restore-keys: ${{ runner.os }}-
37 | path: |
38 | ~/.cabal/packages
39 | ~/.cabal/store
40 | dist-newstyle
41 | - name: Setup NodeJS
42 | uses: actions/setup-node@v4
43 | with:
44 | node-version-file: .nvmrc
45 | cache: yarn
46 | - name: Install packages
47 | run: yarn install --immutable
48 | - name: Publish package to Hackage
49 | run: yarn release
50 | env:
51 | HACKAGE_TOKEN: ${{ secrets.HACKAGE_API_KEY }}
52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | dist-*
3 | cabal-dev
4 | *.o
5 | *.hi
6 | *.hie
7 | *.chi
8 | *.chs.h
9 | *.dyn_o
10 | *.dyn_hi
11 | .hpc
12 | .hsenv
13 | .cabal-sandbox/
14 | cabal.sandbox.config
15 | *.prof
16 | *.aux
17 | *.hp
18 | *.eventlog
19 | .stack-work/
20 | cabal.project.local
21 | cabal.project.local~
22 | .HTF/
23 | .ghc.environment.*
24 |
25 | # VSCode
26 | .vscode
27 |
28 | # NodeJS
29 | node_modules/
30 | .yarn/*
31 | !.yarn/patches
32 | !.yarn/plugins
33 | !.yarn/releases
34 | !.yarn/sdks
35 | !.yarn/versions
36 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 21.6.2
2 |
--------------------------------------------------------------------------------
/.releaserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/semantic-release",
3 | "branches": ["main"],
4 | "plugins": [
5 | "@semantic-release/commit-analyzer",
6 | "@semantic-release/release-notes-generator",
7 | [
8 | "semantic-release-hackage",
9 | {
10 | "packageName": "atomic-write",
11 | "versionPrefix": "0.",
12 | "publishDocumentation": true
13 | }
14 | ],
15 | "@semantic-release/github"
16 | ],
17 | "tagFormat": "v0.${version}"
18 | }
19 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | yarnPath: .yarn/releases/yarn-4.1.0.cjs
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.2.0.7
2 | ### Added
3 | * Binary mode versions of the `atomicWriteFile` and `atomicWriteFileWithMode` functions for ByteString, Text,
4 | String, LazyText and LazyByteString.
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Stack Builders Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/stackbuilders/atomic-write/actions/workflows/build.yml)
2 | [](http://hackage.haskell.org/package/atomic-write)
3 |
4 | [](#contributors-)
5 |
6 |
7 | # Atomic Write
8 |
9 | Atomic Write assists with atomic modification of files using
10 | Haskell. It is a wrapper for using the atomic mv(1) operation which
11 | correctly sets permissions based on the original file, or on system
12 | defaults if no file previously exists.
13 |
14 | ## How it works
15 | On most Unix systems, mv is an atomic operation. This makes it simple to write to a file atomically just by using the mv operation. However, this will destroy the permissions on the original file. This library does the following to preserve permissions while atomically writing to a file:
16 |
17 | If an original file exists, take those permissions and apply them to the temp file before mving the file into place.
18 |
19 | If the original file does not exist, create a following with default permissions (based on the currently-active umask).
20 |
21 | This way, when the file is mv'ed into place, the permissions will be the ones held by the original file.
22 |
23 | This library is based on similar implementations found in common libraries in Ruby and Python:
24 |
25 | - [Ruby on Rails includes a similar method called atomic_write](https://apidock.com/rails/File/atomic_write/class)
26 |
27 | - [Chef includes atomic update functionality](https://github.com/chef/chef/blob/c4631816132fcfefaba3d123a1d0dfe8bc2866bb/lib/chef/file_content_management/deploy/mv_unix.rb#L23:L71)
28 |
29 | - [There is a python library for atomically updating a file](https://github.com/sashka/atomicfile)
30 |
31 | ## Usage
32 |
33 | To use `atomic-write`, import the module corresponding to the type you wish to write atomically, e.g., to write a (strict) ByteString atomically:
34 |
35 | ```import System.AtomicWrite.Writer.ByteString```
36 |
37 | Then you can use the atomicWriteFile function that accepts a FilePath and a ByteString, e.g.:
38 |
39 | ```atomicWriteFile myFilePath myByteString```
40 |
41 | See the
42 | [Haddock documentation](http://hackage.haskell.org/package/atomic-write).
43 |
44 | ## Contributors ✨
45 |
46 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
47 |
48 |
49 |
50 |
51 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
79 |
80 | ## License
81 |
82 | MIT, see [the LICENSE file](LICENSE).
83 |
84 | ## Contributing
85 |
86 | Do you want to contribute to this project? Please take a look at our [contributing guideline](/docs/CONTRIBUTING.md) to know how you can help us build it.
87 |
88 | ---
89 |
90 | [Check out our libraries](https://github.com/stackbuilders/) | [Join our team](https://www.stackbuilders.com/join-us/)
91 |
--------------------------------------------------------------------------------
/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/atomic-write.cabal:
--------------------------------------------------------------------------------
1 | name: atomic-write
2 | version: 0.2.0.7
3 | synopsis: Atomically write to a file
4 | homepage: https://github.com/stackbuilders/atomic-write
5 | description:
6 | .
7 | Atomically write to a file on POSIX-compliant systems while preserving
8 | permissions.
9 | .
10 | On most Unix systems, `mv` is an atomic operation. This makes it simple to write
11 | to a file atomically just by using the mv operation. However, this will
12 | destroy the permissions on the original file. This library does the following
13 | to preserve permissions while atomically writing to a file:
14 | .
15 | * If an original file exists, take those permissions and apply them to the
16 | temp file before `mv`ing the file into place.
17 | .
18 | * If the original file does not exist, create a following with default
19 | permissions (based on the currently-active umask).
20 | .
21 | This way, when the file is `mv`'ed into place, the permissions will be the ones
22 | held by the original file.
23 | .
24 | This library is based on similar implementations found in common libraries in
25 | Ruby and Python:
26 | .
27 | *
28 | .
29 | *
30 | .
31 | *
32 | .
33 | To use `atomic-write`, import the module corresponding to the type you wish to
34 | write atomically, e.g., to write a (strict) ByteString atomically:
35 | .
36 | > import System.AtomicWrite.Writer.ByteString
37 | .
38 | Then you can use the atomicWriteFile function that accepts a `FilePath` and a
39 | `ByteString`, e.g.:
40 | .
41 | > atomicWriteFile myFilePath myByteString
42 | license: MIT
43 | license-file: LICENSE
44 | author: Justin Leitgeb
45 | maintainer: support@stackbuilders.com
46 | copyright: 2015-2019 Stack Builders Inc.
47 | category: System
48 | build-type: Simple
49 | cabal-version: >=1.10
50 | tested-with:
51 | GHC ==8.4.3, GHC ==8.10.7, GHC ==9.4.7
52 |
53 | bug-reports: https://github.com/stackbuilders/atomic-write/issues
54 |
55 | library
56 | exposed-modules: System.AtomicWrite.Writer.ByteString
57 | , System.AtomicWrite.Writer.ByteString.Binary
58 | , System.AtomicWrite.Writer.ByteStringBuilder
59 | , System.AtomicWrite.Writer.LazyByteString
60 | , System.AtomicWrite.Writer.LazyByteString.Binary
61 | , System.AtomicWrite.Writer.String
62 | , System.AtomicWrite.Writer.String.Binary
63 | , System.AtomicWrite.Writer.Text
64 | , System.AtomicWrite.Writer.Text.Binary
65 | , System.AtomicWrite.Writer.LazyText
66 | , System.AtomicWrite.Writer.LazyText.Binary
67 |
68 | other-modules: System.AtomicWrite.Internal
69 |
70 | build-depends: base >= 4.5 && < 5.0
71 | , temporary >= 1.3 && < 1.4
72 | , unix-compat >= 0.5 && < 1.0
73 | , directory >= 1.3 && < 1.4
74 | , filepath >= 1.4 && < 1.6
75 | , text >= 1.2 && < 3.0
76 | , bytestring >= 0.10.4 && < 0.13.0
77 |
78 | hs-source-dirs: src
79 | default-language: Haskell2010
80 | ghc-options: -Wall
81 |
82 |
83 | test-suite atomic-write-test
84 | type: exitcode-stdio-1.0
85 | hs-source-dirs: spec
86 | main-is: Spec.hs
87 |
88 | other-modules: System.AtomicWrite.Writer.ByteStringSpec
89 | , System.AtomicWrite.Writer.ByteString.BinarySpec
90 | , System.AtomicWrite.Writer.ByteStringBuilderSpec
91 | , System.AtomicWrite.Writer.LazyByteStringSpec
92 | , System.AtomicWrite.Writer.LazyByteString.BinarySpec
93 | , System.AtomicWrite.Writer.StringSpec
94 | , System.AtomicWrite.Writer.String.BinarySpec
95 | , System.AtomicWrite.Writer.TextSpec
96 | , System.AtomicWrite.Writer.Text.BinarySpec
97 | , System.AtomicWrite.Writer.LazyTextSpec
98 | , System.AtomicWrite.Writer.LazyText.BinarySpec
99 |
100 | build-depends: base >= 4.5 && < 5.0
101 | , atomic-write
102 | , temporary
103 | , unix-compat
104 | , filepath
105 | , text
106 | , bytestring
107 | , hspec >= 2.5 && < 2.12
108 |
109 | build-tools: hspec-discover >= 2.0 && < 3.0
110 |
111 | default-language: Haskell2010
112 | ghc-options: -Wall
113 |
114 |
115 | source-repository head
116 | type: git
117 | location: git@github.com:stackbuilders/atomic-write.git
118 |
--------------------------------------------------------------------------------
/docs/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of conduct
2 |
3 | ## Purpose
4 | The primary goal of this Code of Conduct is to enable an open and welcoming environment. We pledge to making participation in our project a harassment-free experience for everyone, regardless of gender, sexual
5 | orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
6 |
7 | ## General recommendations
8 | Examples of behavior that contributes to creating a positive environment include:
9 |
10 | - Using welcoming and inclusive language
11 | - Being respectful of differing viewpoints and experiences
12 | - Gracefully accepting constructive criticism
13 | - Focusing on what is best for the community
14 | - Showing empathy towards other community members
15 |
16 | Examples of unacceptable behavior by participants include:
17 |
18 | - The use of sexualized language or imagery and unwelcome sexual attention or advances
19 | - Trolling, insulting/derogatory comments, and personal or political attacks
20 | - Public or private harassment
21 | - Publishing others' private information, such as a physical or electronic address, without explicit permission
22 | - Other conduct which could reasonably be considered inappropriate in a professional setting
23 |
24 | ## Maintainer responsibilities
25 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
26 |
27 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
28 |
29 | ## Scope
30 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
31 |
32 | ## Enforcement
33 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [community@stackbuilders.com](mailto:community@stackbuilders.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
34 |
35 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
--------------------------------------------------------------------------------
/docs/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
2 | Thank you for your interest in contributing to this Stack Builders' library. To contribute, please take our [Code of Conduct](CODE_OF_CONDUCT.md) into account, along with the following recommendations:
3 |
4 | - When submitting contributions to this repository, please make sure to discuss with the maintainer(s) the change you want to make. You can do this through an issue, or by sending an email to [community@stackbuilders.com](mailto:community@stackbuilders.com)
5 |
6 | - Once the change has been discussed with the maintainer(s), feel free to open a Pull Request. Please include a link to the issue you're trying to solve, or a quick summary of the discussed changes.
7 |
8 | - This repo makes use of [semantic-release](https://github.com/semantic-release/semantic-release) with the [semantic-release-hackage](https://github.com/stackbuilders/semantic-release-hackage) puglin. PRs and commits should be following conventional commits formating to properly capture expected changes and determine the version to release.
9 |
10 | - If adding any new features that you think should be considered in the README file, please add that information in your Pull Request.
11 |
12 | - Once you get an approval from any of the maintainers, please merge your Pull Request. Keep in mind that some of our Stack Builders repositories use CI/CD pipelines, so you will need to pass all of the required checks before merging.
13 |
14 | ## Getting help
15 | Contact any of our current maintainers, or send us an email at [community@stackbuilders.com](mailto:community@stackbuilders.com) for more information. Thank you for contributing!
--------------------------------------------------------------------------------
/examples/logger/.gitignore:
--------------------------------------------------------------------------------
1 | .stack-work/
2 | *~
3 |
--------------------------------------------------------------------------------
/examples/logger/README.md:
--------------------------------------------------------------------------------
1 | # logger-example
2 |
3 | This Haskell code example demonstrates a basic logging simulation.
4 |
5 | It emulates a logging operation that sequentially generates a set of log messages. Within a loop, it prints messages to the console, ranging from "Log message 1" to "Log message 5". Each message is printed alongside a confirmation that it has been logged, with a 1-second delay between messages.
6 |
7 | Upon reaching "Log message 5", the process shifts from console output to writing this message into a log file.
8 |
9 |
10 | ## How to use this example
11 |
12 | ### Required dependencies
13 |
14 | - atomic-write
15 | - text
16 | - filepath
17 |
18 |
19 | ### Run Example
20 |
21 | - Use the command: `stack run`
22 |
--------------------------------------------------------------------------------
/examples/logger/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/examples/logger/app.log:
--------------------------------------------------------------------------------
1 | Log message 5
--------------------------------------------------------------------------------
/examples/logger/app/Main.hs:
--------------------------------------------------------------------------------
1 | import System.AtomicWrite.Writer.Text
2 | import qualified Data.Text as T
3 | import qualified Data.Text.IO as TIO
4 | import Control.Monad (forM_)
5 | import Control.Concurrent (forkIO, threadDelay)
6 |
7 | logFilePath :: FilePath
8 | logFilePath = "app.log"
9 |
10 | logMessage :: FilePath -> T.Text -> IO ()
11 | logMessage logFile message = atomicWriteFile logFile message
12 |
13 | simulateLoggingProcess :: FilePath -> IO ()
14 | simulateLoggingProcess logFile = do
15 | forM_ [1..10 :: Int] $ \i -> do
16 | let message = "Log message " <> show i
17 | logMessage logFile (T.pack message)
18 | putStrLn $ "Logged: " <> message
19 | threadDelay 1000000
20 |
21 | main :: IO ()
22 | main = do
23 | TIO.writeFile logFilePath mempty
24 |
25 | _ <- forkIO (simulateLoggingProcess logFilePath)
26 |
27 | threadDelay 5000000
28 |
29 | putStrLn "Log file contents: "
30 | TIO.readFile logFilePath >>= TIO.putStrLn
31 |
--------------------------------------------------------------------------------
/examples/logger/logger-example.cabal:
--------------------------------------------------------------------------------
1 | cabal-version: 2.2
2 |
3 | -- This file has been generated from package.yaml by hpack version 0.36.0.
4 | --
5 | -- see: https://github.com/sol/hpack
6 |
7 | name: logger-example
8 | version: 0.1.0.0
9 | description: Please see the README on GitHub at
10 | homepage: https://github.com/githubuser/logger-example#readme
11 | bug-reports: https://github.com/githubuser/logger-example/issues
12 | author: Author name here
13 | maintainer: example@example.com
14 | copyright: 2023 Author name here
15 | license: BSD-3-Clause
16 | license-file: LICENSE
17 | build-type: Simple
18 | extra-source-files:
19 | README.md
20 | CHANGELOG.md
21 |
22 | source-repository head
23 | type: git
24 | location: https://github.com/githubuser/logger-example
25 |
26 | library
27 | exposed-modules:
28 | Lib
29 | other-modules:
30 | Paths_logger_example
31 | autogen-modules:
32 | Paths_logger_example
33 | hs-source-dirs:
34 | src
35 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints
36 | build-depends:
37 | atomic-write >=0.2
38 | , base >=4.7 && <5
39 | , filepath >=1.4 && <2
40 | , text >=2.0
41 | default-language: Haskell2010
42 |
43 | executable logger-example-exe
44 | main-is: Main.hs
45 | other-modules:
46 | Paths_logger_example
47 | autogen-modules:
48 | Paths_logger_example
49 | hs-source-dirs:
50 | app
51 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
52 | build-depends:
53 | atomic-write >=0.2
54 | , base >=4.7 && <5
55 | , filepath >=1.4 && <2
56 | , logger-example
57 | , text >=2.0
58 | default-language: Haskell2010
59 |
60 | test-suite logger-example-test
61 | type: exitcode-stdio-1.0
62 | main-is: Spec.hs
63 | other-modules:
64 | Paths_logger_example
65 | autogen-modules:
66 | Paths_logger_example
67 | hs-source-dirs:
68 | test
69 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
70 | build-depends:
71 | atomic-write >=0.2
72 | , base >=4.7 && <5
73 | , filepath >=1.4 && <2
74 | , logger-example
75 | , text >=2.0
76 | default-language: Haskell2010
77 |
--------------------------------------------------------------------------------
/examples/logger/package.yaml:
--------------------------------------------------------------------------------
1 | name: logger-example
2 | version: 0.1.0.0
3 | github: "stackbuilders/atomic-write"
4 | license: BSD-3-Clause
5 | author: "@BarbDMC"
6 | maintainer: "amejia@stackbuilders.com"
7 | copyright: "2023 @BarbDMC"
8 |
9 | extra-source-files:
10 | - README.md
11 | - CHANGELOG.md
12 |
13 | # Metadata used when publishing your package
14 | # synopsis: Short description of your package
15 | # category: Web
16 |
17 | # To avoid duplicated efforts in documentation and dealing with the
18 | # complications of embedding Haddock markup inside cabal files, it is
19 | # common to point users to the README.md file.
20 | description: Please see the README on GitHub at
21 |
22 | dependencies:
23 | - base >= 4.7 && < 5
24 | - atomic-write >= 0.2
25 | - text >= 2.0
26 | - filepath >= 1.4 && < 2
27 |
28 | ghc-options:
29 | - -Wall
30 | - -Wcompat
31 | - -Widentities
32 | - -Wincomplete-record-updates
33 | - -Wincomplete-uni-patterns
34 | - -Wmissing-export-lists
35 | - -Wmissing-home-modules
36 | - -Wpartial-fields
37 | - -Wredundant-constraints
38 |
39 | library:
40 | source-dirs: src
41 |
42 | executables:
43 | logger-example-exe:
44 | main: Main.hs
45 | source-dirs: app
46 | ghc-options:
47 | - -threaded
48 | - -rtsopts
49 | - -with-rtsopts=-N
50 | dependencies:
51 | - logger-example
52 |
53 |
--------------------------------------------------------------------------------
/examples/logger/stack.yaml:
--------------------------------------------------------------------------------
1 |
2 | resolver:
3 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
4 |
5 |
6 | packages:
7 | - .
8 |
--------------------------------------------------------------------------------
/examples/logger/stack.yaml.lock:
--------------------------------------------------------------------------------
1 | # This file was autogenerated by Stack.
2 | # You should not edit this file by hand.
3 | # For more information, please see the documentation at:
4 | # https://docs.haskellstack.org/en/stable/lock_files
5 |
6 | packages: []
7 | snapshots:
8 | - completed:
9 | sha256: fb482b8e2d5d061cdda4ba1da2957c012740c893a5ee1c1b99001adae7b1fbe7
10 | size: 640046
11 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
12 | original:
13 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "atomic-write",
3 | "version": "0.0.0",
4 | "description": "Writes files atomically in Haskell while preserving permissions",
5 | "repository": "git@github.com:stackbuilders/atomic-write.git",
6 | "author": "Stack Builders ",
7 | "license": "MIT",
8 | "engines": {
9 | "node": "21.6.2"
10 | },
11 | "scripts": {
12 | "release": "semantic-release"
13 | },
14 | "devDependencies": {
15 | "semantic-release": "^24.0.0",
16 | "semantic-release-hackage": "^1.1.2"
17 | },
18 | "packageManager": "yarn@4.1.0"
19 | }
20 |
--------------------------------------------------------------------------------
/spec/Spec.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -F -pgmF hspec-discover #-}
2 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/ByteString/BinarySpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.ByteString.BinarySpec (spec) where
2 |
3 |
4 | import Test.Hspec (Spec, describe,
5 | it, shouldBe)
6 |
7 | import System.AtomicWrite.Writer.ByteString.Binary (atomicWriteFile, atomicWriteFileWithMode)
8 |
9 | import System.FilePath.Posix (joinPath)
10 | import System.IO.Temp (withSystemTempDirectory)
11 | import System.PosixCompat.Files (fileMode,
12 | getFileStatus,
13 | setFileCreationMask,
14 | setFileMode)
15 |
16 | import Data.ByteString.Char8 (pack)
17 |
18 |
19 | spec :: Spec
20 | spec = do
21 | describe "atomicWriteFile" $ do
22 | it "writes the contents to a file" $
23 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
24 |
25 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
26 |
27 | atomicWriteFile path $ pack "just testing"
28 | contents <- readFile path
29 |
30 | contents `shouldBe` "just testing"
31 | it "preserves the permissions of original file, regardless of umask" $
32 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
33 | let filePath = joinPath [tmpDir, "testFile"]
34 |
35 | writeFile filePath "initial contents"
36 | setFileMode filePath 0o100644
37 |
38 | newStat <- getFileStatus filePath
39 | fileMode newStat `shouldBe` 0o100644
40 |
41 | -- New files are created with 100600 perms.
42 | _ <- setFileCreationMask 0o100066
43 |
44 | -- Create a new file once different mask is set and make sure that mask
45 | -- is applied.
46 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
47 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
48 | fileMode sanityCheckStat `shouldBe` 0o100600
49 |
50 | -- Since we move, this makes the new file assume the filemask of 0600
51 | atomicWriteFile filePath $ pack "new contents"
52 |
53 | resultStat <- getFileStatus filePath
54 |
55 | -- reset mask to not break subsequent specs
56 | _ <- setFileCreationMask 0o100022
57 |
58 | -- Fails when using atomic mv command unless apply perms on initial file
59 | fileMode resultStat `shouldBe` 0o100644
60 |
61 |
62 | it "creates a new file with permissions based on active umask" $
63 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
64 | let
65 | filePath = joinPath [tmpDir, "testFile"]
66 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
67 |
68 | -- Set somewhat distinctive defaults for test
69 | _ <- setFileCreationMask 0o100171
70 |
71 | -- We don't know what the default file permissions are, so create a
72 | -- file to sample them.
73 | writeFile sampleFilePath "I'm being written to sample permissions"
74 |
75 | newStat <- getFileStatus sampleFilePath
76 | fileMode newStat `shouldBe` 0o100606
77 |
78 | atomicWriteFile filePath $ pack "new contents"
79 |
80 | resultStat <- getFileStatus filePath
81 |
82 | -- reset mask to not break subsequent specs
83 | _ <- setFileCreationMask 0o100022
84 |
85 | -- The default tempfile permissions are 0600, so this fails unless we
86 | -- make sure that the default umask is relied on for creation of the
87 | -- tempfile.
88 | fileMode resultStat `shouldBe` 0o100606
89 | describe "atomicWriteFileWithMode" $ do
90 | it "writes contents to a file" $
91 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
92 |
93 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
94 |
95 | atomicWriteFileWithMode 0o100777 path $ pack "just testing"
96 |
97 | contents <- readFile path
98 |
99 | contents `shouldBe` "just testing"
100 |
101 |
102 | it "changes the permissions of a previously created file, regardless of umask" $
103 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
104 | let filePath = joinPath [tmpDir, "testFile"]
105 |
106 | writeFile filePath "initial contents"
107 | setFileMode filePath 0o100644
108 |
109 | newStat <- getFileStatus filePath
110 | fileMode newStat `shouldBe` 0o100644
111 |
112 | -- New files are created with 100600 perms.
113 | _ <- setFileCreationMask 0o100066
114 |
115 | -- Create a new file once different mask is set and make sure that mask
116 | -- is applied.
117 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
118 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
119 | fileMode sanityCheckStat `shouldBe` 0o100600
120 |
121 | -- Since we move, this makes the new file assume the filemask of 0600
122 | atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"
123 |
124 | resultStat <- getFileStatus filePath
125 |
126 | -- reset mask to not break subsequent specs
127 | _ <- setFileCreationMask 0o100022
128 |
129 | -- Fails when using atomic mv command unless apply perms on initial file
130 | fileMode resultStat `shouldBe` 0o100655
131 |
132 |
133 | it "creates a new file with specified permissions" $
134 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
135 | let
136 | filePath = joinPath [tmpDir, "testFile"]
137 | atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"
138 |
139 | resultStat <- getFileStatus filePath
140 |
141 | fileMode resultStat `shouldBe` 0o100606
142 |
143 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/ByteStringBuilderSpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.ByteStringBuilderSpec (spec) where
2 |
3 | import Test.Hspec (it, describe, shouldBe, Spec)
4 |
5 | import System.AtomicWrite.Writer.ByteStringBuilder (atomicWriteFile, atomicWriteFileWithMode)
6 |
7 | import System.IO.Temp (withSystemTempDirectory)
8 | import System.FilePath.Posix (joinPath)
9 | import System.PosixCompat.Files
10 | (setFileMode, setFileCreationMask, getFileStatus, fileMode)
11 |
12 | import Data.ByteString.Builder (lazyByteString)
13 |
14 | import Data.ByteString.Lazy.Char8 (pack)
15 |
16 | spec :: Spec
17 | spec = do
18 | describe "atomicWriteFile" $ do
19 | it "writes contents to a file" $
20 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
21 |
22 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
23 |
24 | atomicWriteFile path $ lazyByteString $ pack "just testing"
25 | contents <- readFile path
26 |
27 | contents `shouldBe` "just testing"
28 | it "preserves the permissions of original file, regardless of umask" $
29 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
30 | let filePath = joinPath [tmpDir, "testFile"]
31 |
32 | writeFile filePath "initial contents"
33 | setFileMode filePath 0o100644
34 |
35 | newStat <- getFileStatus filePath
36 | fileMode newStat `shouldBe` 0o100644
37 |
38 | -- New files are created with 100600 perms.
39 | _ <- setFileCreationMask 0o100066
40 |
41 | -- Create a new file once different mask is set and make sure that mask
42 | -- is applied.
43 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
44 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
45 | fileMode sanityCheckStat `shouldBe` 0o100600
46 |
47 | -- Since we move, this makes the new file assume the filemask of 0600
48 | atomicWriteFile filePath $ lazyByteString $ pack "new contents"
49 |
50 | resultStat <- getFileStatus filePath
51 |
52 | -- reset mask to not break subsequent specs
53 | _ <- setFileCreationMask 0o100022
54 |
55 | -- Fails when using atomic mv command unless apply perms on initial file
56 | fileMode resultStat `shouldBe` 0o100644
57 |
58 |
59 | it "creates a new file with permissions based on active umask" $
60 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
61 | let
62 | filePath = joinPath [tmpDir, "testFile"]
63 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
64 |
65 | -- Set somewhat distinctive defaults for test
66 | _ <- setFileCreationMask 0o100171
67 |
68 | -- We don't know what the default file permissions are, so create a
69 | -- file to sample them.
70 | writeFile sampleFilePath "I'm being written to sample permissions"
71 |
72 | newStat <- getFileStatus sampleFilePath
73 | fileMode newStat `shouldBe` 0o100606
74 |
75 | atomicWriteFile filePath $ lazyByteString $ pack "new contents"
76 |
77 | resultStat <- getFileStatus filePath
78 |
79 | -- reset mask to not break subsequent specs
80 | _ <- setFileCreationMask 0o100022
81 |
82 | -- The default tempfile permissions are 0600, so this fails unless we
83 | -- make sure that the default umask is relied on for creation of the
84 | -- tempfile.
85 | fileMode resultStat `shouldBe` 0o100606
86 | describe "atomicWriteFileWithMode" $ do
87 | it "writes contents to a file" $
88 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
89 |
90 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
91 |
92 | atomicWriteFileWithMode 0o100777 path $ lazyByteString $ pack "just testing"
93 |
94 | contents <- readFile path
95 |
96 | contents `shouldBe` "just testing"
97 |
98 |
99 | it "changes the permissions of a previously created file, regardless of umask" $
100 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
101 | let filePath = joinPath [tmpDir, "testFile"]
102 |
103 | writeFile filePath "initial contents"
104 | setFileMode filePath 0o100644
105 |
106 | newStat <- getFileStatus filePath
107 | fileMode newStat `shouldBe` 0o100644
108 |
109 | -- New files are created with 100600 perms.
110 | _ <- setFileCreationMask 0o100066
111 |
112 | -- Create a new file once different mask is set and make sure that mask
113 | -- is applied.
114 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
115 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
116 | fileMode sanityCheckStat `shouldBe` 0o100600
117 |
118 | -- Since we move, this makes the new file assume the filemask of 0600
119 | atomicWriteFileWithMode 0o100655 filePath $ lazyByteString $ pack "new contents"
120 |
121 | resultStat <- getFileStatus filePath
122 |
123 | -- reset mask to not break subsequent specs
124 | _ <- setFileCreationMask 0o100022
125 |
126 | -- Fails when using atomic mv command unless apply perms on initial file
127 | fileMode resultStat `shouldBe` 0o100655
128 |
129 |
130 | it "creates a new file with specified permissions" $
131 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
132 | let
133 | filePath = joinPath [tmpDir, "testFile"]
134 | atomicWriteFileWithMode 0o100606 filePath $ lazyByteString $ pack "new contents"
135 |
136 | resultStat <- getFileStatus filePath
137 |
138 | fileMode resultStat `shouldBe` 0o100606
139 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/ByteStringSpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.ByteStringSpec (spec) where
2 |
3 | import Test.Hspec (Spec, describe, it,
4 | shouldBe)
5 |
6 | import System.AtomicWrite.Writer.ByteString (atomicWriteFile,
7 | atomicWriteFileWithMode)
8 |
9 | import System.FilePath.Posix (joinPath)
10 | import System.IO.Temp (withSystemTempDirectory)
11 | import System.PosixCompat.Files (fileMode, getFileStatus,
12 | setFileCreationMask,
13 | setFileMode)
14 |
15 | import Data.ByteString.Char8 (pack)
16 |
17 | spec :: Spec
18 | spec = do
19 | describe "atomicWriteFile" $ do
20 | it "writes contents to a file" $
21 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
22 |
23 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
24 |
25 | atomicWriteFile path $ pack "just testing"
26 | contents <- readFile path
27 |
28 | contents `shouldBe` "just testing"
29 | it "preserves the permissions of original file, regardless of umask" $
30 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
31 | let filePath = joinPath [tmpDir, "testFile"]
32 |
33 | writeFile filePath "initial contents"
34 | setFileMode filePath 0o100644
35 |
36 | newStat <- getFileStatus filePath
37 | fileMode newStat `shouldBe` 0o100644
38 |
39 | -- New files are created with 100600 perms.
40 | _ <- setFileCreationMask 0o100066
41 |
42 | -- Create a new file once different mask is set and make sure that mask
43 | -- is applied.
44 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
45 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
46 | fileMode sanityCheckStat `shouldBe` 0o100600
47 |
48 | -- Since we move, this makes the new file assume the filemask of 0600
49 | atomicWriteFile filePath $ pack "new contents"
50 |
51 | resultStat <- getFileStatus filePath
52 |
53 | -- reset mask to not break subsequent specs
54 | _ <- setFileCreationMask 0o100022
55 |
56 | -- Fails when using atomic mv command unless apply perms on initial file
57 | fileMode resultStat `shouldBe` 0o100644
58 |
59 |
60 | it "creates a new file with permissions based on active umask" $
61 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
62 | let
63 | filePath = joinPath [tmpDir, "testFile"]
64 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
65 |
66 | -- Set somewhat distinctive defaults for test
67 | _ <- setFileCreationMask 0o100171
68 |
69 | -- We don't know what the default file permissions are, so create a
70 | -- file to sample them.
71 | writeFile sampleFilePath "I'm being written to sample permissions"
72 |
73 | newStat <- getFileStatus sampleFilePath
74 | fileMode newStat `shouldBe` 0o100606
75 |
76 | atomicWriteFile filePath $ pack "new contents"
77 |
78 | resultStat <- getFileStatus filePath
79 |
80 | -- reset mask to not break subsequent specs
81 | _ <- setFileCreationMask 0o100022
82 |
83 | -- The default tempfile permissions are 0600, so this fails unless we
84 | -- make sure that the default umask is relied on for creation of the
85 | -- tempfile.
86 | fileMode resultStat `shouldBe` 0o100606
87 | describe "atomicWriteFileWithMode" $ do
88 | it "writes contents to a file" $
89 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
90 |
91 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
92 |
93 | atomicWriteFileWithMode 0o100777 path $ pack "just testing"
94 |
95 | contents <- readFile path
96 |
97 | contents `shouldBe` "just testing"
98 |
99 |
100 | it "changes the permissions of a previously created file, regardless of umask" $
101 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
102 | let filePath = joinPath [tmpDir, "testFile"]
103 |
104 | writeFile filePath "initial contents"
105 | setFileMode filePath 0o100644
106 |
107 | newStat <- getFileStatus filePath
108 | fileMode newStat `shouldBe` 0o100644
109 |
110 | -- New files are created with 100600 perms.
111 | _ <- setFileCreationMask 0o100066
112 |
113 | -- Create a new file once different mask is set and make sure that mask
114 | -- is applied.
115 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
116 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
117 | fileMode sanityCheckStat `shouldBe` 0o100600
118 |
119 | -- Since we move, this makes the new file assume the filemask of 0600
120 | atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"
121 |
122 | resultStat <- getFileStatus filePath
123 |
124 | -- reset mask to not break subsequent specs
125 | _ <- setFileCreationMask 0o100022
126 |
127 | -- Fails when using atomic mv command unless apply perms on initial file
128 | fileMode resultStat `shouldBe` 0o100655
129 |
130 |
131 | it "creates a new file with specified permissions" $
132 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
133 | let
134 | filePath = joinPath [tmpDir, "testFile"]
135 | atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"
136 |
137 | resultStat <- getFileStatus filePath
138 |
139 | fileMode resultStat `shouldBe` 0o100606
140 |
141 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/LazyByteString/BinarySpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.LazyByteString.BinarySpec (spec) where
2 |
3 | import Test.Hspec (Spec,
4 | describe, it,
5 | shouldBe)
6 |
7 | import System.AtomicWrite.Writer.LazyByteString.Binary (atomicWriteFile,
8 | atomicWriteFileWithMode)
9 |
10 | import System.FilePath.Posix (joinPath)
11 | import System.IO.Temp (withSystemTempDirectory)
12 | import System.PosixCompat.Files (fileMode,
13 | getFileStatus,
14 | setFileCreationMask,
15 | setFileMode)
16 |
17 | import Data.ByteString.Lazy.Char8 (pack)
18 |
19 | spec :: Spec
20 | spec = do
21 | describe "atomicWriteFile" $ do
22 | it "writes contents to a file" $
23 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
24 |
25 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
26 |
27 | atomicWriteFile path $ pack "just testing"
28 | contents <- readFile path
29 |
30 | contents `shouldBe` "just testing"
31 | it "preserves the permissions of original file, regardless of umask" $
32 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
33 | let filePath = joinPath [tmpDir, "testFile"]
34 |
35 | writeFile filePath "initial contents"
36 | setFileMode filePath 0o100644
37 |
38 | newStat <- getFileStatus filePath
39 | fileMode newStat `shouldBe` 0o100644
40 |
41 | -- New files are created with 100600 perms.
42 | _ <- setFileCreationMask 0o100066
43 |
44 | -- Create a new file once different mask is set and make sure that mask
45 | -- is applied.
46 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
47 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
48 | fileMode sanityCheckStat `shouldBe` 0o100600
49 |
50 | -- Since we move, this makes the new file assume the filemask of 0600
51 | atomicWriteFile filePath $ pack "new contents"
52 |
53 | resultStat <- getFileStatus filePath
54 |
55 | -- reset mask to not break subsequent specs
56 | _ <- setFileCreationMask 0o100022
57 |
58 | -- Fails when using atomic mv command unless apply perms on initial file
59 | fileMode resultStat `shouldBe` 0o100644
60 |
61 |
62 | it "creates a new file with permissions based on active umask" $
63 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
64 | let
65 | filePath = joinPath [tmpDir, "testFile"]
66 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
67 |
68 | -- Set somewhat distinctive defaults for test
69 | _ <- setFileCreationMask 0o100171
70 |
71 | -- We don't know what the default file permissions are, so create a
72 | -- file to sample them.
73 | writeFile sampleFilePath "I'm being written to sample permissions"
74 |
75 | newStat <- getFileStatus sampleFilePath
76 | fileMode newStat `shouldBe` 0o100606
77 |
78 | atomicWriteFile filePath $ pack "new contents"
79 |
80 | resultStat <- getFileStatus filePath
81 |
82 | -- reset mask to not break subsequent specs
83 | _ <- setFileCreationMask 0o100022
84 |
85 | -- The default tempfile permissions are 0600, so this fails unless we
86 | -- make sure that the default umask is relied on for creation of the
87 | -- tempfile.
88 | fileMode resultStat `shouldBe` 0o100606
89 | describe "atomicWriteFileWithMode" $ do
90 | it "writes contents to a file" $
91 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
92 |
93 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
94 |
95 | atomicWriteFileWithMode 0o100777 path $ pack "just testing"
96 |
97 | contents <- readFile path
98 |
99 | contents `shouldBe` "just testing"
100 |
101 |
102 | it "changes the permissions of a previously created file, regardless of umask" $
103 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
104 | let filePath = joinPath [tmpDir, "testFile"]
105 |
106 | writeFile filePath "initial contents"
107 | setFileMode filePath 0o100644
108 |
109 | newStat <- getFileStatus filePath
110 | fileMode newStat `shouldBe` 0o100644
111 |
112 | -- New files are created with 100600 perms.
113 | _ <- setFileCreationMask 0o100066
114 |
115 | -- Create a new file once different mask is set and make sure that mask
116 | -- is applied.
117 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
118 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
119 | fileMode sanityCheckStat `shouldBe` 0o100600
120 |
121 | -- Since we move, this makes the new file assume the filemask of 0600
122 | atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"
123 |
124 | resultStat <- getFileStatus filePath
125 |
126 | -- reset mask to not break subsequent specs
127 | _ <- setFileCreationMask 0o100022
128 |
129 | -- Fails when using atomic mv command unless apply perms on initial file
130 | fileMode resultStat `shouldBe` 0o100655
131 |
132 |
133 | it "creates a new file with specified permissions" $
134 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
135 | let
136 | filePath = joinPath [tmpDir, "testFile"]
137 | atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"
138 |
139 | resultStat <- getFileStatus filePath
140 |
141 | fileMode resultStat `shouldBe` 0o100606
142 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/LazyByteStringSpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.LazyByteStringSpec (spec) where
2 |
3 | import Test.Hspec (it, describe, shouldBe, Spec)
4 |
5 | import System.AtomicWrite.Writer.LazyByteString (atomicWriteFile, atomicWriteFileWithMode)
6 |
7 | import System.IO.Temp (withSystemTempDirectory)
8 | import System.FilePath.Posix (joinPath)
9 | import System.PosixCompat.Files
10 | (setFileMode, setFileCreationMask, getFileStatus, fileMode)
11 |
12 | import Data.ByteString.Lazy.Char8 (pack)
13 |
14 | spec :: Spec
15 | spec = do
16 | describe "atomicWriteFile" $ do
17 | it "writes contents to a file" $
18 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
19 |
20 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
21 |
22 | atomicWriteFile path $ pack "just testing"
23 | contents <- readFile path
24 |
25 | contents `shouldBe` "just testing"
26 | it "preserves the permissions of original file, regardless of umask" $
27 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
28 | let filePath = joinPath [tmpDir, "testFile"]
29 |
30 | writeFile filePath "initial contents"
31 | setFileMode filePath 0o100644
32 |
33 | newStat <- getFileStatus filePath
34 | fileMode newStat `shouldBe` 0o100644
35 |
36 | -- New files are created with 100600 perms.
37 | _ <- setFileCreationMask 0o100066
38 |
39 | -- Create a new file once different mask is set and make sure that mask
40 | -- is applied.
41 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
42 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
43 | fileMode sanityCheckStat `shouldBe` 0o100600
44 |
45 | -- Since we move, this makes the new file assume the filemask of 0600
46 | atomicWriteFile filePath $ pack "new contents"
47 |
48 | resultStat <- getFileStatus filePath
49 |
50 | -- reset mask to not break subsequent specs
51 | _ <- setFileCreationMask 0o100022
52 |
53 | -- Fails when using atomic mv command unless apply perms on initial file
54 | fileMode resultStat `shouldBe` 0o100644
55 |
56 |
57 | it "creates a new file with permissions based on active umask" $
58 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
59 | let
60 | filePath = joinPath [tmpDir, "testFile"]
61 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
62 |
63 | -- Set somewhat distinctive defaults for test
64 | _ <- setFileCreationMask 0o100171
65 |
66 | -- We don't know what the default file permissions are, so create a
67 | -- file to sample them.
68 | writeFile sampleFilePath "I'm being written to sample permissions"
69 |
70 | newStat <- getFileStatus sampleFilePath
71 | fileMode newStat `shouldBe` 0o100606
72 |
73 | atomicWriteFile filePath $ pack "new contents"
74 |
75 | resultStat <- getFileStatus filePath
76 |
77 | -- reset mask to not break subsequent specs
78 | _ <- setFileCreationMask 0o100022
79 |
80 | -- The default tempfile permissions are 0600, so this fails unless we
81 | -- make sure that the default umask is relied on for creation of the
82 | -- tempfile.
83 | fileMode resultStat `shouldBe` 0o100606
84 | describe "atomicWriteFileWithMode" $ do
85 | it "writes contents to a file" $
86 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
87 |
88 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
89 |
90 | atomicWriteFileWithMode 0o100777 path $ pack "just testing"
91 |
92 | contents <- readFile path
93 |
94 | contents `shouldBe` "just testing"
95 |
96 |
97 | it "changes the permissions of a previously created file, regardless of umask" $
98 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
99 | let filePath = joinPath [tmpDir, "testFile"]
100 |
101 | writeFile filePath "initial contents"
102 | setFileMode filePath 0o100644
103 |
104 | newStat <- getFileStatus filePath
105 | fileMode newStat `shouldBe` 0o100644
106 |
107 | -- New files are created with 100600 perms.
108 | _ <- setFileCreationMask 0o100066
109 |
110 | -- Create a new file once different mask is set and make sure that mask
111 | -- is applied.
112 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
113 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
114 | fileMode sanityCheckStat `shouldBe` 0o100600
115 |
116 | -- Since we move, this makes the new file assume the filemask of 0600
117 | atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"
118 |
119 | resultStat <- getFileStatus filePath
120 |
121 | -- reset mask to not break subsequent specs
122 | _ <- setFileCreationMask 0o100022
123 |
124 | -- Fails when using atomic mv command unless apply perms on initial file
125 | fileMode resultStat `shouldBe` 0o100655
126 |
127 |
128 | it "creates a new file with specified permissions" $
129 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
130 | let
131 | filePath = joinPath [tmpDir, "testFile"]
132 | atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"
133 |
134 | resultStat <- getFileStatus filePath
135 |
136 | fileMode resultStat `shouldBe` 0o100606
137 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/LazyText/BinarySpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.LazyText.BinarySpec (spec) where
2 |
3 | import Test.Hspec (Spec, describe, it,
4 | shouldBe)
5 |
6 | import System.AtomicWrite.Writer.LazyText.Binary (atomicWriteFile, atomicWriteFileWithMode)
7 |
8 | import System.FilePath.Posix (joinPath)
9 | import System.IO.Temp (withSystemTempDirectory)
10 | import System.PosixCompat.Files (fileMode,
11 | getFileStatus,
12 | setFileCreationMask,
13 | setFileMode)
14 |
15 | import Data.Text.Lazy (pack)
16 |
17 | spec :: Spec
18 | spec = do
19 | describe "atomicWriteFile" $ do
20 | it "writes contents to a file" $
21 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
22 |
23 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
24 |
25 | atomicWriteFile path $ pack "just testing"
26 | contents <- readFile path
27 |
28 | contents `shouldBe` "just testing"
29 | it "preserves the permissions of original file, regardless of umask" $
30 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
31 | let filePath = joinPath [tmpDir, "testFile"]
32 |
33 | writeFile filePath "initial contents"
34 | setFileMode filePath 0o100644
35 |
36 | newStat <- getFileStatus filePath
37 | fileMode newStat `shouldBe` 0o100644
38 |
39 | -- New files are created with 100600 perms.
40 | _ <- setFileCreationMask 0o100066
41 |
42 | -- Create a new file once different mask is set and make sure that mask
43 | -- is applied.
44 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
45 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
46 | fileMode sanityCheckStat `shouldBe` 0o100600
47 |
48 | -- Since we move, this makes the new file assume the filemask of 0600
49 | atomicWriteFile filePath $ pack "new contents"
50 |
51 | resultStat <- getFileStatus filePath
52 |
53 | -- reset mask to not break subsequent specs
54 | _ <- setFileCreationMask 0o100022
55 |
56 | -- Fails when using atomic mv command unless apply perms on initial file
57 | fileMode resultStat `shouldBe` 0o100644
58 |
59 |
60 | it "creates a new file with permissions based on active umask" $
61 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
62 | let
63 | filePath = joinPath [tmpDir, "testFile"]
64 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
65 |
66 | -- Set somewhat distinctive defaults for test
67 | _ <- setFileCreationMask 0o100171
68 |
69 | -- We don't know what the default file permissions are, so create a
70 | -- file to sample them.
71 | writeFile sampleFilePath "I'm being written to sample permissions"
72 |
73 | newStat <- getFileStatus sampleFilePath
74 | fileMode newStat `shouldBe` 0o100606
75 |
76 | atomicWriteFile filePath $ pack "new contents"
77 |
78 | resultStat <- getFileStatus filePath
79 |
80 | -- reset mask to not break subsequent specs
81 | _ <- setFileCreationMask 0o100022
82 |
83 | -- The default tempfile permissions are 0600, so this fails unless we
84 | -- make sure that the default umask is relied on for creation of the
85 | -- tempfile.
86 | fileMode resultStat `shouldBe` 0o100606
87 | describe "atomicWriteFileWithMode" $ do
88 | it "writes contents to a file" $
89 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
90 |
91 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
92 |
93 | atomicWriteFileWithMode 0o100777 path $ pack "just testing"
94 |
95 | contents <- readFile path
96 |
97 | contents `shouldBe` "just testing"
98 |
99 |
100 | it "changes the permissions of a previously created file, regardless of umask" $
101 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
102 | let filePath = joinPath [tmpDir, "testFile"]
103 |
104 | writeFile filePath "initial contents"
105 | setFileMode filePath 0o100644
106 |
107 | newStat <- getFileStatus filePath
108 | fileMode newStat `shouldBe` 0o100644
109 |
110 | -- New files are created with 100600 perms.
111 | _ <- setFileCreationMask 0o100066
112 |
113 | -- Create a new file once different mask is set and make sure that mask
114 | -- is applied.
115 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
116 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
117 | fileMode sanityCheckStat `shouldBe` 0o100600
118 |
119 | -- Since we move, this makes the new file assume the filemask of 0600
120 | atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"
121 |
122 | resultStat <- getFileStatus filePath
123 |
124 | -- reset mask to not break subsequent specs
125 | _ <- setFileCreationMask 0o100022
126 |
127 | -- Fails when using atomic mv command unless apply perms on initial file
128 | fileMode resultStat `shouldBe` 0o100655
129 |
130 |
131 | it "creates a new file with specified permissions" $
132 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
133 | let
134 | filePath = joinPath [tmpDir, "testFile"]
135 | atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"
136 |
137 | resultStat <- getFileStatus filePath
138 |
139 | fileMode resultStat `shouldBe` 0o100606
140 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/LazyTextSpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.LazyTextSpec (spec) where
2 |
3 | import Test.Hspec (it, describe, shouldBe, Spec)
4 |
5 | import System.AtomicWrite.Writer.LazyText (atomicWriteFile, atomicWriteFileWithMode)
6 |
7 | import System.IO.Temp (withSystemTempDirectory)
8 | import System.FilePath.Posix (joinPath)
9 | import System.PosixCompat.Files
10 | (setFileMode, setFileCreationMask, getFileStatus, fileMode)
11 |
12 | import Data.Text.Lazy (pack)
13 |
14 | spec :: Spec
15 | spec = do
16 | describe "atomicWriteFile" $ do
17 | it "writes contents to a file" $
18 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
19 |
20 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
21 |
22 | atomicWriteFile path $ pack "just testing"
23 | contents <- readFile path
24 |
25 | contents `shouldBe` "just testing"
26 | it "preserves the permissions of original file, regardless of umask" $
27 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
28 | let filePath = joinPath [tmpDir, "testFile"]
29 |
30 | writeFile filePath "initial contents"
31 | setFileMode filePath 0o100644
32 |
33 | newStat <- getFileStatus filePath
34 | fileMode newStat `shouldBe` 0o100644
35 |
36 | -- New files are created with 100600 perms.
37 | _ <- setFileCreationMask 0o100066
38 |
39 | -- Create a new file once different mask is set and make sure that mask
40 | -- is applied.
41 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
42 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
43 | fileMode sanityCheckStat `shouldBe` 0o100600
44 |
45 | -- Since we move, this makes the new file assume the filemask of 0600
46 | atomicWriteFile filePath $ pack "new contents"
47 |
48 | resultStat <- getFileStatus filePath
49 |
50 | -- reset mask to not break subsequent specs
51 | _ <- setFileCreationMask 0o100022
52 |
53 | -- Fails when using atomic mv command unless apply perms on initial file
54 | fileMode resultStat `shouldBe` 0o100644
55 |
56 |
57 | it "creates a new file with permissions based on active umask" $
58 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
59 | let
60 | filePath = joinPath [tmpDir, "testFile"]
61 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
62 |
63 | -- Set somewhat distinctive defaults for test
64 | _ <- setFileCreationMask 0o100171
65 |
66 | -- We don't know what the default file permissions are, so create a
67 | -- file to sample them.
68 | writeFile sampleFilePath "I'm being written to sample permissions"
69 |
70 | newStat <- getFileStatus sampleFilePath
71 | fileMode newStat `shouldBe` 0o100606
72 |
73 | atomicWriteFile filePath $ pack "new contents"
74 |
75 | resultStat <- getFileStatus filePath
76 |
77 | -- reset mask to not break subsequent specs
78 | _ <- setFileCreationMask 0o100022
79 |
80 | -- The default tempfile permissions are 0600, so this fails unless we
81 | -- make sure that the default umask is relied on for creation of the
82 | -- tempfile.
83 | fileMode resultStat `shouldBe` 0o100606
84 | describe "atomicWriteFileWithMode" $ do
85 | it "writes contents to a file" $
86 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
87 |
88 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
89 |
90 | atomicWriteFileWithMode 0o100777 path $ pack "just testing"
91 |
92 | contents <- readFile path
93 |
94 | contents `shouldBe` "just testing"
95 |
96 |
97 | it "changes the permissions of a previously created file, regardless of umask" $
98 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
99 | let filePath = joinPath [tmpDir, "testFile"]
100 |
101 | writeFile filePath "initial contents"
102 | setFileMode filePath 0o100644
103 |
104 | newStat <- getFileStatus filePath
105 | fileMode newStat `shouldBe` 0o100644
106 |
107 | -- New files are created with 100600 perms.
108 | _ <- setFileCreationMask 0o100066
109 |
110 | -- Create a new file once different mask is set and make sure that mask
111 | -- is applied.
112 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
113 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
114 | fileMode sanityCheckStat `shouldBe` 0o100600
115 |
116 | -- Since we move, this makes the new file assume the filemask of 0600
117 | atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"
118 |
119 | resultStat <- getFileStatus filePath
120 |
121 | -- reset mask to not break subsequent specs
122 | _ <- setFileCreationMask 0o100022
123 |
124 | -- Fails when using atomic mv command unless apply perms on initial file
125 | fileMode resultStat `shouldBe` 0o100655
126 |
127 |
128 | it "creates a new file with specified permissions" $
129 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
130 | let
131 | filePath = joinPath [tmpDir, "testFile"]
132 | atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"
133 |
134 | resultStat <- getFileStatus filePath
135 |
136 | fileMode resultStat `shouldBe` 0o100606
137 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/String/BinarySpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.String.BinarySpec (spec) where
2 |
3 | import Test.Hspec (Spec, describe, it,
4 | shouldBe)
5 |
6 | import System.AtomicWrite.Writer.String.Binary (atomicWriteFile, atomicWriteFileWithMode)
7 |
8 | import System.FilePath (joinPath)
9 | import System.IO.Temp (withSystemTempDirectory)
10 | import System.PosixCompat.Files (fileMode,
11 | getFileStatus,
12 | setFileCreationMask,
13 | setFileMode)
14 |
15 |
16 | {-# ANN module "HLint: ignore Reduce duplication" #-}
17 |
18 | spec :: Spec
19 | spec = do
20 | describe "atomicWriteFile" $ do
21 | it "writes contents to a file" $
22 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
23 |
24 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
25 |
26 | atomicWriteFile path "just testing"
27 |
28 | contents <- readFile path
29 |
30 | contents `shouldBe` "just testing"
31 |
32 |
33 | it "preserves the permissions of original file, regardless of umask" $
34 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
35 | let filePath = joinPath [tmpDir, "testFile"]
36 |
37 | writeFile filePath "initial contents"
38 | setFileMode filePath 0o100644
39 |
40 | newStat <- getFileStatus filePath
41 | fileMode newStat `shouldBe` 0o100644
42 |
43 | -- New files are created with 100600 perms.
44 | _ <- setFileCreationMask 0o100066
45 |
46 | -- Create a new file once different mask is set and make sure that mask
47 | -- is applied.
48 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
49 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
50 | fileMode sanityCheckStat `shouldBe` 0o100600
51 |
52 | -- Since we move, this makes the new file assume the filemask of 0600
53 | atomicWriteFile filePath "new contents"
54 |
55 | resultStat <- getFileStatus filePath
56 |
57 | -- reset mask to not break subsequent specs
58 | _ <- setFileCreationMask 0o100022
59 |
60 | -- Fails when using atomic mv command unless apply perms on initial file
61 | fileMode resultStat `shouldBe` 0o100644
62 |
63 |
64 | it "creates a new file with permissions based on active umask" $
65 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
66 | let
67 | filePath = joinPath [tmpDir, "testFile"]
68 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
69 |
70 | -- Set somewhat distinctive defaults for test
71 | _ <- setFileCreationMask 0o100171
72 |
73 | -- We don't know what the default file permissions are, so create a
74 | -- file to sample them.
75 | writeFile sampleFilePath "I'm being written to sample permissions"
76 |
77 | newStat <- getFileStatus sampleFilePath
78 | fileMode newStat `shouldBe` 0o100606
79 |
80 | atomicWriteFile filePath "new contents"
81 |
82 | resultStat <- getFileStatus filePath
83 |
84 | -- reset mask to not break subsequent specs
85 | _ <- setFileCreationMask 0o100022
86 |
87 | -- The default tempfile permissions are 0600, so this fails unless we
88 | -- make sure that the default umask is relied on for creation of the
89 | -- tempfile.
90 | fileMode resultStat `shouldBe` 0o100606
91 | describe "atomicWriteFileWithMode" $ do
92 | it "writes contents to a file" $
93 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
94 |
95 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
96 |
97 | atomicWriteFileWithMode 0o100777 path "just testing"
98 |
99 | contents <- readFile path
100 |
101 | contents `shouldBe` "just testing"
102 |
103 |
104 | it "changes the permissions of a previously created file, regardless of umask" $
105 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
106 | let filePath = joinPath [tmpDir, "testFile"]
107 |
108 | writeFile filePath "initial contents"
109 | setFileMode filePath 0o100644
110 |
111 | newStat <- getFileStatus filePath
112 | fileMode newStat `shouldBe` 0o100644
113 |
114 | -- New files are created with 100600 perms.
115 | _ <- setFileCreationMask 0o100066
116 |
117 | -- Create a new file once different mask is set and make sure that mask
118 | -- is applied.
119 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
120 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
121 | fileMode sanityCheckStat `shouldBe` 0o100600
122 |
123 | -- Since we move, this makes the new file assume the filemask of 0600
124 | atomicWriteFileWithMode 0o100655 filePath "new contents"
125 |
126 | resultStat <- getFileStatus filePath
127 |
128 | -- reset mask to not break subsequent specs
129 | _ <- setFileCreationMask 0o100022
130 |
131 | -- Fails when using atomic mv command unless apply perms on initial file
132 | fileMode resultStat `shouldBe` 0o100655
133 |
134 |
135 | it "creates a new file with specified permissions" $
136 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
137 | let
138 | filePath = joinPath [tmpDir, "testFile"]
139 | atomicWriteFileWithMode 0o100606 filePath "new contents"
140 |
141 | resultStat <- getFileStatus filePath
142 |
143 | fileMode resultStat `shouldBe` 0o100606
144 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/StringSpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.StringSpec (spec) where
2 |
3 | import Test.Hspec (Spec, describe, it, shouldBe)
4 |
5 | import System.AtomicWrite.Writer.String (atomicWriteFile,
6 | atomicWriteFileWithMode)
7 |
8 | import System.FilePath (joinPath)
9 | import System.IO.Temp (withSystemTempDirectory)
10 | import System.PosixCompat.Files (fileMode, getFileStatus,
11 | setFileCreationMask,
12 | setFileMode)
13 |
14 |
15 | {-# ANN module "HLint: ignore Reduce duplication" #-}
16 |
17 | spec :: Spec
18 | spec = do
19 | describe "atomicWriteFile" $ do
20 | it "writes contents to a file" $
21 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
22 |
23 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
24 |
25 | atomicWriteFile path "just testing"
26 |
27 | contents <- readFile path
28 |
29 | contents `shouldBe` "just testing"
30 |
31 |
32 | it "preserves the permissions of original file, regardless of umask" $
33 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
34 | let filePath = joinPath [tmpDir, "testFile"]
35 |
36 | writeFile filePath "initial contents"
37 | setFileMode filePath 0o100644
38 |
39 | newStat <- getFileStatus filePath
40 | fileMode newStat `shouldBe` 0o100644
41 |
42 | -- New files are created with 100600 perms.
43 | _ <- setFileCreationMask 0o100066
44 |
45 | -- Create a new file once different mask is set and make sure that mask
46 | -- is applied.
47 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
48 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
49 | fileMode sanityCheckStat `shouldBe` 0o100600
50 |
51 | -- Since we move, this makes the new file assume the filemask of 0600
52 | atomicWriteFile filePath "new contents"
53 |
54 | resultStat <- getFileStatus filePath
55 |
56 | -- reset mask to not break subsequent specs
57 | _ <- setFileCreationMask 0o100022
58 |
59 | -- Fails when using atomic mv command unless apply perms on initial file
60 | fileMode resultStat `shouldBe` 0o100644
61 |
62 |
63 | it "creates a new file with permissions based on active umask" $
64 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
65 | let
66 | filePath = joinPath [tmpDir, "testFile"]
67 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
68 |
69 | -- Set somewhat distinctive defaults for test
70 | _ <- setFileCreationMask 0o100171
71 |
72 | -- We don't know what the default file permissions are, so create a
73 | -- file to sample them.
74 | writeFile sampleFilePath "I'm being written to sample permissions"
75 |
76 | newStat <- getFileStatus sampleFilePath
77 | fileMode newStat `shouldBe` 0o100606
78 |
79 | atomicWriteFile filePath "new contents"
80 |
81 | resultStat <- getFileStatus filePath
82 |
83 | -- reset mask to not break subsequent specs
84 | _ <- setFileCreationMask 0o100022
85 |
86 | -- The default tempfile permissions are 0600, so this fails unless we
87 | -- make sure that the default umask is relied on for creation of the
88 | -- tempfile.
89 | fileMode resultStat `shouldBe` 0o100606
90 | describe "atomicWriteFileWithMode" $ do
91 | it "writes contents to a file" $
92 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
93 |
94 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
95 |
96 | atomicWriteFileWithMode 0o100777 path "just testing"
97 |
98 | contents <- readFile path
99 |
100 | contents `shouldBe` "just testing"
101 |
102 |
103 | it "changes the permissions of a previously created file, regardless of umask" $
104 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
105 | let filePath = joinPath [tmpDir, "testFile"]
106 |
107 | writeFile filePath "initial contents"
108 | setFileMode filePath 0o100644
109 |
110 | newStat <- getFileStatus filePath
111 | fileMode newStat `shouldBe` 0o100644
112 |
113 | -- New files are created with 100600 perms.
114 | _ <- setFileCreationMask 0o100066
115 |
116 | -- Create a new file once different mask is set and make sure that mask
117 | -- is applied.
118 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
119 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
120 | fileMode sanityCheckStat `shouldBe` 0o100600
121 |
122 | -- Since we move, this makes the new file assume the filemask of 0600
123 | atomicWriteFileWithMode 0o100655 filePath "new contents"
124 |
125 | resultStat <- getFileStatus filePath
126 |
127 | -- reset mask to not break subsequent specs
128 | _ <- setFileCreationMask 0o100022
129 |
130 | -- Fails when using atomic mv command unless apply perms on initial file
131 | fileMode resultStat `shouldBe` 0o100655
132 |
133 |
134 | it "creates a new file with specified permissions" $
135 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
136 | let
137 | filePath = joinPath [tmpDir, "testFile"]
138 | atomicWriteFileWithMode 0o100606 filePath "new contents"
139 |
140 | resultStat <- getFileStatus filePath
141 |
142 | fileMode resultStat `shouldBe` 0o100606
143 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/Text/BinarySpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.Text.BinarySpec (spec) where
2 |
3 | import Test.Hspec (Spec, describe, it,
4 | shouldBe)
5 |
6 | import System.AtomicWrite.Writer.Text.Binary (atomicWriteFile,
7 | atomicWriteFileWithMode)
8 |
9 | import System.FilePath.Posix (joinPath)
10 | import System.IO.Temp (withSystemTempDirectory)
11 | import System.PosixCompat.Files (fileMode, getFileStatus,
12 | setFileCreationMask,
13 | setFileMode)
14 |
15 | import Data.Text (pack)
16 |
17 | spec :: Spec
18 | spec = do
19 | describe "atomicWriteFile" $ do
20 | it "writes contents to a file" $
21 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
22 |
23 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
24 |
25 | atomicWriteFile path $ pack "just testing"
26 | contents <- readFile path
27 |
28 | contents `shouldBe` "just testing"
29 | it "preserves the permissions of original file, regardless of umask" $
30 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
31 | let filePath = joinPath [tmpDir, "testFile"]
32 |
33 | writeFile filePath "initial contents"
34 | setFileMode filePath 0o100644
35 |
36 | newStat <- getFileStatus filePath
37 | fileMode newStat `shouldBe` 0o100644
38 |
39 | -- New files are created with 100600 perms.
40 | _ <- setFileCreationMask 0o100066
41 |
42 | -- Create a new file once different mask is set and make sure that mask
43 | -- is applied.
44 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
45 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
46 | fileMode sanityCheckStat `shouldBe` 0o100600
47 |
48 | -- Since we move, this makes the new file assume the filemask of 0600
49 | atomicWriteFile filePath $ pack "new contents"
50 |
51 | resultStat <- getFileStatus filePath
52 |
53 | -- reset mask to not break subsequent specs
54 | _ <- setFileCreationMask 0o100022
55 |
56 | -- Fails when using atomic mv command unless apply perms on initial file
57 | fileMode resultStat `shouldBe` 0o100644
58 |
59 |
60 | it "creates a new file with permissions based on active umask" $
61 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
62 | let
63 | filePath = joinPath [tmpDir, "testFile"]
64 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
65 |
66 | -- Set somewhat distinctive defaults for test
67 | _ <- setFileCreationMask 0o100171
68 |
69 | -- We don't know what the default file permissions are, so create a
70 | -- file to sample them.
71 | writeFile sampleFilePath "I'm being written to sample permissions"
72 |
73 | newStat <- getFileStatus sampleFilePath
74 | fileMode newStat `shouldBe` 0o100606
75 |
76 | atomicWriteFile filePath $ pack "new contents"
77 |
78 | resultStat <- getFileStatus filePath
79 |
80 | -- reset mask to not break subsequent specs
81 | _ <- setFileCreationMask 0o100022
82 |
83 | -- The default tempfile permissions are 0600, so this fails unless we
84 | -- make sure that the default umask is relied on for creation of the
85 | -- tempfile.
86 | fileMode resultStat `shouldBe` 0o100606
87 | describe "atomicWriteFileWithMode" $ do
88 | it "writes contents to a file" $
89 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
90 |
91 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
92 |
93 | atomicWriteFileWithMode 0o100777 path $ pack "just testing"
94 |
95 | contents <- readFile path
96 |
97 | contents `shouldBe` "just testing"
98 |
99 |
100 | it "changes the permissions of a previously created file, regardless of umask" $
101 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
102 | let filePath = joinPath [tmpDir, "testFile"]
103 |
104 | writeFile filePath "initial contents"
105 | setFileMode filePath 0o100644
106 |
107 | newStat <- getFileStatus filePath
108 | fileMode newStat `shouldBe` 0o100644
109 |
110 | -- New files are created with 100600 perms.
111 | _ <- setFileCreationMask 0o100066
112 |
113 | -- Create a new file once different mask is set and make sure that mask
114 | -- is applied.
115 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
116 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
117 | fileMode sanityCheckStat `shouldBe` 0o100600
118 |
119 | -- Since we move, this makes the new file assume the filemask of 0600
120 | atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"
121 |
122 | resultStat <- getFileStatus filePath
123 |
124 | -- reset mask to not break subsequent specs
125 | _ <- setFileCreationMask 0o100022
126 |
127 | -- Fails when using atomic mv command unless apply perms on initial file
128 | fileMode resultStat `shouldBe` 0o100655
129 |
130 |
131 | it "creates a new file with specified permissions" $
132 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
133 | let
134 | filePath = joinPath [tmpDir, "testFile"]
135 | atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"
136 |
137 | resultStat <- getFileStatus filePath
138 |
139 | fileMode resultStat `shouldBe` 0o100606
140 |
--------------------------------------------------------------------------------
/spec/System/AtomicWrite/Writer/TextSpec.hs:
--------------------------------------------------------------------------------
1 | module System.AtomicWrite.Writer.TextSpec (spec) where
2 |
3 | import Test.Hspec (it, describe, shouldBe, Spec)
4 |
5 | import System.AtomicWrite.Writer.Text (atomicWriteFile, atomicWriteFileWithMode)
6 |
7 | import System.IO.Temp (withSystemTempDirectory)
8 | import System.FilePath.Posix (joinPath)
9 | import System.PosixCompat.Files
10 | (setFileMode, setFileCreationMask, getFileStatus, fileMode)
11 |
12 | import Data.Text (pack)
13 |
14 | spec :: Spec
15 | spec = do
16 | describe "atomicWriteFile" $ do
17 | it "writes contents to a file" $
18 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
19 |
20 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
21 |
22 | atomicWriteFile path $ pack "just testing"
23 | contents <- readFile path
24 |
25 | contents `shouldBe` "just testing"
26 | it "preserves the permissions of original file, regardless of umask" $
27 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
28 | let filePath = joinPath [tmpDir, "testFile"]
29 |
30 | writeFile filePath "initial contents"
31 | setFileMode filePath 0o100644
32 |
33 | newStat <- getFileStatus filePath
34 | fileMode newStat `shouldBe` 0o100644
35 |
36 | -- New files are created with 100600 perms.
37 | _ <- setFileCreationMask 0o100066
38 |
39 | -- Create a new file once different mask is set and make sure that mask
40 | -- is applied.
41 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
42 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
43 | fileMode sanityCheckStat `shouldBe` 0o100600
44 |
45 | -- Since we move, this makes the new file assume the filemask of 0600
46 | atomicWriteFile filePath $ pack "new contents"
47 |
48 | resultStat <- getFileStatus filePath
49 |
50 | -- reset mask to not break subsequent specs
51 | _ <- setFileCreationMask 0o100022
52 |
53 | -- Fails when using atomic mv command unless apply perms on initial file
54 | fileMode resultStat `shouldBe` 0o100644
55 |
56 |
57 | it "creates a new file with permissions based on active umask" $
58 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
59 | let
60 | filePath = joinPath [tmpDir, "testFile"]
61 | sampleFilePath = joinPath [tmpDir, "sampleFile"]
62 |
63 | -- Set somewhat distinctive defaults for test
64 | _ <- setFileCreationMask 0o100171
65 |
66 | -- We don't know what the default file permissions are, so create a
67 | -- file to sample them.
68 | writeFile sampleFilePath "I'm being written to sample permissions"
69 |
70 | newStat <- getFileStatus sampleFilePath
71 | fileMode newStat `shouldBe` 0o100606
72 |
73 | atomicWriteFile filePath $ pack "new contents"
74 |
75 | resultStat <- getFileStatus filePath
76 |
77 | -- reset mask to not break subsequent specs
78 | _ <- setFileCreationMask 0o100022
79 |
80 | -- The default tempfile permissions are 0600, so this fails unless we
81 | -- make sure that the default umask is relied on for creation of the
82 | -- tempfile.
83 | fileMode resultStat `shouldBe` 0o100606
84 | describe "atomicWriteFileWithMode" $ do
85 | it "writes contents to a file" $
86 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
87 |
88 | let path = joinPath [ tmpDir, "writeTest.tmp" ]
89 |
90 | atomicWriteFileWithMode 0o100777 path $ pack "just testing"
91 |
92 | contents <- readFile path
93 |
94 | contents `shouldBe` "just testing"
95 |
96 |
97 | it "changes the permissions of a previously created file, regardless of umask" $
98 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
99 | let filePath = joinPath [tmpDir, "testFile"]
100 |
101 | writeFile filePath "initial contents"
102 | setFileMode filePath 0o100644
103 |
104 | newStat <- getFileStatus filePath
105 | fileMode newStat `shouldBe` 0o100644
106 |
107 | -- New files are created with 100600 perms.
108 | _ <- setFileCreationMask 0o100066
109 |
110 | -- Create a new file once different mask is set and make sure that mask
111 | -- is applied.
112 | writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"
113 | sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]
114 | fileMode sanityCheckStat `shouldBe` 0o100600
115 |
116 | -- Since we move, this makes the new file assume the filemask of 0600
117 | atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"
118 |
119 | resultStat <- getFileStatus filePath
120 |
121 | -- reset mask to not break subsequent specs
122 | _ <- setFileCreationMask 0o100022
123 |
124 | -- Fails when using atomic mv command unless apply perms on initial file
125 | fileMode resultStat `shouldBe` 0o100655
126 |
127 |
128 | it "creates a new file with specified permissions" $
129 | withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do
130 | let
131 | filePath = joinPath [tmpDir, "testFile"]
132 | atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"
133 |
134 | resultStat <- getFileStatus filePath
135 |
136 | fileMode resultStat `shouldBe` 0o100606
137 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Internal.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Internal
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to create a temporary file with correct permissions
11 | -- atomically.
12 |
13 | module System.AtomicWrite.Internal where
14 |
15 | import System.Directory (doesFileExist, renameFile)
16 | import System.FilePath (takeDirectory)
17 | import System.IO (Handle, hClose, hSetBinaryMode,
18 | openTempFile,
19 | openTempFileWithDefaultPermissions)
20 | import System.Posix.Types (FileMode)
21 | import System.PosixCompat.Files (fileMode, getFileStatus, setFileMode)
22 |
23 | -- | Returns a temporary file with permissions correctly set. Chooses
24 | -- either previously-set permissions if the file that we're writing
25 | -- to existed, or permissions following the current umask.
26 | tempFileFor ::
27 | FilePath -- ^ The target filepath that we will replace atomically.
28 | -> IO (FilePath, Handle)
29 | tempFileFor targetFilePath =
30 |
31 | doesFileExist targetFilePath >>=
32 | tmpFile targetFilePath (takeDirectory targetFilePath) "atomic.write"
33 |
34 | where
35 |
36 | tmpFile :: FilePath -> FilePath -> String -> Bool -> IO (FilePath, Handle)
37 | tmpFile targetPath workingDirectory template previousExisted =
38 |
39 | if previousExisted then
40 | openTempFile workingDirectory template >>=
41 |
42 | \(tmpPath, handle) ->
43 |
44 | getFileStatus targetPath >>= setFileMode tmpPath . fileMode >>
45 |
46 | return (tmpPath, handle)
47 |
48 | else
49 | openTempFileWithDefaultPermissions workingDirectory template
50 |
51 |
52 | closeAndRename :: Handle -> FilePath -> FilePath -> IO ()
53 | closeAndRename tmpHandle tempFile destFile =
54 | hClose tmpHandle >> renameFile tempFile destFile
55 |
56 | maybeSetFileMode :: FilePath -> Maybe FileMode -> IO ()
57 | maybeSetFileMode path =
58 | maybe
59 | ( return () )
60 | ( \mode -> setFileMode path mode )
61 |
62 |
63 | -- Helper Function
64 | atomicWriteFileMaybeModeText ::
65 | Maybe FileMode -- ^ The mode to set the file to
66 | -> FilePath -- ^ The path where the file will be updated or created
67 | -> (Handle -> a -> IO ()) -- ^ The function to use to write on the file
68 | -> a -- ^ The content to write to the file
69 | -> IO ()
70 | atomicWriteFileMaybeModeText mmode path hF text =
71 | tempFileFor path >>= \(tmpPath, h) -> hSetBinaryMode h False
72 | >> hF h text
73 | >> closeAndRename h tmpPath path
74 | >> maybeSetFileMode path mmode
75 | -- Helper Function
76 | atomicWriteFileMaybeModeBinary ::
77 | Maybe FileMode -- ^ The mode to set the file to
78 | -> FilePath -- ^ The path where the file will be updated or created
79 | -> (Handle -> a -> IO ()) -- ^ The function to use to write on the file
80 | -> a -- ^ The content to write to the file
81 | -> IO ()
82 | atomicWriteFileMaybeModeBinary mmode path hF text =
83 | tempFileFor path >>= \(tmpPath, h) -> hSetBinaryMode h True
84 | >> hF h text
85 | >> closeAndRename h tmpPath path
86 | >> maybeSetFileMode path mmode
87 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/ByteString.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.ByteString
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a ByteString
11 | -- to a file in text mode.
12 |
13 | module System.AtomicWrite.Writer.ByteString (atomicWriteFile, atomicWriteFileWithMode) where
14 |
15 | import System.AtomicWrite.Internal (atomicWriteFileMaybeModeText)
16 |
17 | import System.Posix.Types (FileMode)
18 |
19 | import Data.ByteString (ByteString, hPutStr)
20 |
21 |
22 | -- | Creates or modifies a file atomically on POSIX-compliant
23 | -- systems while preserving permissions.
24 | atomicWriteFile ::
25 | FilePath -- ^ The path where the file will be updated or created
26 | -> ByteString -- ^ The content to write to the file
27 | -> IO ()
28 | atomicWriteFile = atomicWriteFileMaybeMode Nothing
29 |
30 | -- | Creates or modifies a file atomically on POSIX-compliant
31 | -- systems and updates permissions.
32 | atomicWriteFileWithMode ::
33 | FileMode
34 | -> FilePath -- ^ The path where the file will be updated or created
35 | -> ByteString -- ^ The content to write to the file
36 | -> IO ()
37 | atomicWriteFileWithMode = atomicWriteFileMaybeMode . Just
38 |
39 | -- | Helper function
40 | atomicWriteFileMaybeMode ::
41 | Maybe FileMode
42 | -> FilePath -- ^ The path where the file will be updated or created
43 | -> ByteString -- ^ The content to write to the file
44 | -> IO ()
45 | atomicWriteFileMaybeMode mmode path = atomicWriteFileMaybeModeText mmode path hPutStr
46 |
47 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/ByteString/Binary.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.ByteString.Binary
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a ByteString
11 | -- to a file open in binary mode
12 |
13 | module System.AtomicWrite.Writer.ByteString.Binary (atomicWriteFile, atomicWriteFileWithMode) where
14 |
15 | import System.AtomicWrite.Internal (atomicWriteFileMaybeModeBinary)
16 |
17 | import System.Posix.Types (FileMode)
18 |
19 | import Data.ByteString (ByteString, hPutStr)
20 |
21 |
22 | -- | Creates or modifies a file atomically on POSIX-compliant
23 | -- systems while preserving permissions. The file is opened in
24 | -- binary mode.
25 | atomicWriteFile ::
26 | FilePath -- ^ The path where the file will be updated or created
27 | -> ByteString -- ^ The content to write to the file
28 | -> IO ()
29 | atomicWriteFile = atomicWriteFileMaybeMode Nothing
30 |
31 | -- | Creates or modifies a file atomically on POSIX-compliant
32 | -- systems and updates permissions. The file is opened in binary
33 | -- mode.
34 | atomicWriteFileWithMode ::
35 | FileMode
36 | -> FilePath -- ^ The path where the file will be updated or created
37 | -> ByteString -- ^ The content to write to the file
38 | -> IO ()
39 | atomicWriteFileWithMode mode =
40 | atomicWriteFileMaybeMode $ Just mode
41 |
42 | -- | Helper function for opening the file in binary mode.
43 | atomicWriteFileMaybeMode ::
44 | Maybe FileMode
45 | -> FilePath -- ^ The path where the file will be updated or created
46 | -> ByteString -- ^ The content to write to the file
47 | -> IO ()
48 | atomicWriteFileMaybeMode mmode path = atomicWriteFileMaybeModeBinary mmode path hPutStr
49 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/ByteStringBuilder.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.ByteStringBuilder
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a ByteStringBuilder
11 | -- to a file.
12 |
13 | module System.AtomicWrite.Writer.ByteStringBuilder (atomicWriteFile, atomicWriteFileWithMode) where
14 |
15 | import System.AtomicWrite.Internal (closeAndRename, maybeSetFileMode,
16 | tempFileFor)
17 |
18 | import Data.ByteString.Builder (Builder, hPutBuilder)
19 |
20 | import GHC.IO.Handle (BufferMode (BlockBuffering),
21 | hSetBinaryMode, hSetBuffering)
22 |
23 | import System.Posix.Types (FileMode)
24 |
25 | -- | Creates or modifies a file atomically on POSIX-compliant
26 | -- systems while preserving permissions.
27 | atomicWriteFile ::
28 | FilePath -- ^ The path where the file will be updated or created
29 | -> Builder -- ^ The content to write to the file
30 | -> IO ()
31 | atomicWriteFile =
32 | atomicWriteFileMaybeMode Nothing
33 |
34 | -- | Creates or modifies a file atomically on POSIX-compliant
35 | -- systems and updates permissions.
36 | atomicWriteFileWithMode ::
37 | FileMode
38 | -> FilePath -- ^ The path where the file will be updated or created
39 | -> Builder -- ^ The content to write to the file
40 | -> IO ()
41 | atomicWriteFileWithMode mode =
42 | atomicWriteFileMaybeMode $ Just mode
43 |
44 | -- Helper function
45 | atomicWriteFileMaybeMode ::
46 | Maybe FileMode
47 | -> FilePath -- ^ The path where the file will be updated or created
48 | -> Builder -- ^ The content to write to the file
49 | -> IO ()
50 | atomicWriteFileMaybeMode mmode path builder = do
51 | (temppath, h) <- tempFileFor path
52 |
53 | -- Recommendations for binary and buffering are from the
54 | -- Data.ByteString.Builder docs:
55 | -- http://hackage.haskell.org/package/bytestring-0.10.2.0/docs/Data-ByteString-Builder.html#v:hPutBuilder
56 | hSetBinaryMode h True
57 | hSetBuffering h (BlockBuffering Nothing)
58 |
59 | hPutBuilder h builder
60 |
61 | closeAndRename h temppath path
62 |
63 | -- set new permissions if a FileMode was provided
64 | maybeSetFileMode path mmode
65 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/LazyByteString.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.LazyByteString
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a Lazy ByteString
11 | -- to a file.
12 |
13 | module System.AtomicWrite.Writer.LazyByteString (atomicWriteFile, atomicWriteFileWithMode) where
14 |
15 | import System.AtomicWrite.Internal (atomicWriteFileMaybeModeText)
16 |
17 | import Data.ByteString.Lazy (ByteString, hPutStr)
18 |
19 | import System.Posix.Types (FileMode)
20 |
21 | -- | Creates or modifies a file atomically on POSIX-compliant
22 | -- systems while preserving permissions.
23 | atomicWriteFile ::
24 | FilePath -- ^ The path where the file will be updated or created
25 | -> ByteString -- ^ The content to write to the file
26 | -> IO ()
27 | atomicWriteFile =
28 | atomicWriteFileMaybeMode Nothing
29 |
30 | -- | Creates or modifies a file atomically on
31 | -- POSIX-compliant systems and updates permissions
32 | atomicWriteFileWithMode ::
33 | FileMode -- ^ The mode to set the file to
34 | -> FilePath -- ^ The path where the file will be updated or created
35 | -> ByteString -- ^ The content to write to the file
36 | -> IO ()
37 | atomicWriteFileWithMode =
38 | atomicWriteFileMaybeMode . Just
39 |
40 | -- Helper Function
41 | atomicWriteFileMaybeMode ::
42 | Maybe FileMode -- ^ The mode to set the file to
43 | -> FilePath -- ^ The path where the file will be updated or created
44 | -> ByteString -- ^ The content to write to the file
45 | -> IO ()
46 | atomicWriteFileMaybeMode mmode path = atomicWriteFileMaybeModeText mmode path hPutStr
47 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/LazyByteString/Binary.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.LazyByteString.Binary
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a Lazy ByteString
11 | -- to a file in binary mode.
12 |
13 | module System.AtomicWrite.Writer.LazyByteString.Binary (atomicWriteFile, atomicWriteFileWithMode) where
14 |
15 | import System.AtomicWrite.Internal (atomicWriteFileMaybeModeBinary)
16 |
17 | import Data.ByteString.Lazy (ByteString, hPutStr)
18 |
19 | import System.Posix.Types (FileMode)
20 |
21 |
22 | -- | Creates or modifies a file atomically on POSIX-compliant
23 | -- systems while preserving permissions.
24 | atomicWriteFile ::
25 | FilePath -- ^ The path where the file will be updated or created
26 | -> ByteString -- ^ The content to write to the file
27 | -> IO ()
28 | atomicWriteFile =
29 | atomicWriteFileMaybeMode Nothing
30 |
31 | -- | Creates or modifies a file atomically on
32 | -- POSIX-compliant systems and updates permissions
33 | atomicWriteFileWithMode ::
34 | FileMode -- ^ The mode to set the file to
35 | -> FilePath -- ^ The path where the file will be updated or created
36 | -> ByteString -- ^ The content to write to the file
37 | -> IO ()
38 | atomicWriteFileWithMode = atomicWriteFileMaybeMode . Just
39 |
40 | -- Helper Function
41 | atomicWriteFileMaybeMode ::
42 | Maybe FileMode -- ^ The mode to set the file to
43 | -> FilePath -- ^ The path where the file will be updated or created
44 | -> ByteString -- ^ The content to write to the file
45 | -> IO ()
46 | atomicWriteFileMaybeMode mmode path = atomicWriteFileMaybeModeBinary mmode path hPutStr
47 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/LazyText.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.LazyText
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a Text
11 | -- to a file.
12 |
13 | module System.AtomicWrite.Writer.LazyText (atomicWriteFile, atomicWriteFileWithMode) where
14 |
15 | import System.AtomicWrite.Internal (atomicWriteFileMaybeModeText)
16 |
17 | import Data.Text.Lazy (Text)
18 |
19 | import Data.Text.Lazy.IO (hPutStr)
20 |
21 | import System.Posix.Types (FileMode)
22 |
23 | -- | Creates a file atomically on POSIX-compliant
24 | -- systems while preserving permissions.
25 | atomicWriteFile ::
26 | FilePath -- ^ The path where the file will be updated or created
27 | -> Text -- ^ The content to write to the file
28 | -> IO ()
29 | atomicWriteFile =
30 | atomicWriteFileMaybeMode Nothing
31 |
32 | -- | Creates or modifies a file atomically on
33 | -- POSIX-compliant systems and updates permissions
34 | atomicWriteFileWithMode ::
35 | FileMode -- ^ The mode to set the file to
36 | -> FilePath -- ^ The path where the file will be updated or created
37 | -> Text -- ^ The content to write to the file
38 | -> IO ()
39 | atomicWriteFileWithMode =
40 | atomicWriteFileMaybeMode . Just
41 |
42 | -- Helper Function
43 | atomicWriteFileMaybeMode ::
44 | Maybe FileMode -- ^ The mode to set the file to
45 | -> FilePath -- ^ The path where the file will be updated or created
46 | -> Text -- ^ The content to write to the file
47 | -> IO ()
48 | atomicWriteFileMaybeMode mmode path = atomicWriteFileMaybeModeText mmode path hPutStr
49 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/LazyText/Binary.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.LazyText.Binary
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a Text
11 | -- to a file in binary mode.
12 |
13 | module System.AtomicWrite.Writer.LazyText.Binary (atomicWriteFile, atomicWriteFileWithMode) where
14 |
15 | import System.AtomicWrite.Internal (atomicWriteFileMaybeModeBinary)
16 |
17 | import Data.Text.Lazy (Text)
18 |
19 | import Data.Text.Lazy.IO (hPutStr)
20 |
21 | import System.Posix.Types (FileMode)
22 |
23 |
24 | -- | Creates a file atomically on POSIX-compliant
25 | -- systems while preserving permissions.
26 | atomicWriteFile ::
27 | FilePath -- ^ The path where the file will be updated or created
28 | -> Text -- ^ The content to write to the file
29 | -> IO ()
30 | atomicWriteFile =
31 | atomicWriteFileMaybeMode Nothing
32 |
33 | -- | Creates or modifies a file atomically on
34 | -- POSIX-compliant systems and updates permissions
35 | atomicWriteFileWithMode ::
36 | FileMode -- ^ The mode to set the file to
37 | -> FilePath -- ^ The path where the file will be updated or created
38 | -> Text -- ^ The content to write to the file
39 | -> IO ()
40 | atomicWriteFileWithMode = atomicWriteFileMaybeMode . Just
41 |
42 | -- Helper Function
43 | atomicWriteFileMaybeMode ::
44 | Maybe FileMode -- ^ The mode to set the file to
45 | -> FilePath -- ^ The path where the file will be updated or created
46 | -> Text -- ^ The content to write to the file
47 | -> IO ()
48 | atomicWriteFileMaybeMode mmode path = atomicWriteFileMaybeModeBinary mmode path hPutStr
49 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/String.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.String
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a String
11 | -- to a file.
12 |
13 | module System.AtomicWrite.Writer.String (atomicWriteFile, atomicWithFile, atomicWriteFileWithMode, atomicWithFileAndMode) where
14 |
15 | import System.AtomicWrite.Internal (closeAndRename, maybeSetFileMode,
16 | tempFileFor)
17 |
18 | import System.IO (Handle, hPutStr)
19 |
20 | import System.Posix.Types (FileMode)
21 |
22 | -- | Creates or modifies a file atomically on POSIX-compliant
23 | -- systems while preserving permissions.
24 | atomicWriteFile ::
25 | FilePath -- ^ The path where the file will be updated or created
26 | -> String -- ^ The content to write to the file
27 | -> IO ()
28 | atomicWriteFile = (. flip hPutStr) . atomicWithFile
29 |
30 |
31 | -- | Creates or modifies a file atomically on
32 | -- POSIX-compliant systems and updates permissions
33 | atomicWriteFileWithMode ::
34 | FileMode -- ^ The mode to set the file to
35 | -> FilePath -- ^ The path where the file will be updated or created
36 | -> String -- ^ The content to write to the file
37 | -> IO ()
38 | atomicWriteFileWithMode mode = ( . flip hPutStr)
39 | . atomicWithFileAndMode mode
40 |
41 | -- | A general version of 'atomicWriteFile'
42 | atomicWithFile :: FilePath -> (Handle -> IO ()) -> IO ()
43 | atomicWithFile = atomicWithFileAndMaybeMode Nothing
44 |
45 | -- | A general version of 'atomicWriteFileWithMode'
46 | atomicWithFileAndMode :: FileMode
47 | -> FilePath
48 | -> (Handle -> IO ())
49 | -> IO ()
50 | atomicWithFileAndMode = atomicWithFileAndMaybeMode . Just
51 |
52 | -- | Helper function
53 | atomicWithFileAndMaybeMode :: Maybe FileMode
54 | -> FilePath
55 | -> (Handle -> IO ())
56 | -> IO ()
57 | atomicWithFileAndMaybeMode mmode path action =
58 | tempFileFor path >>= \(tmpPath, h) -> action h
59 | >> closeAndRename h tmpPath path
60 | >> maybeSetFileMode path mmode
61 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/String/Binary.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.String.Binary
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a String
11 | -- to a file in binary mode.
12 |
13 | module System.AtomicWrite.Writer.String.Binary (atomicWriteFile, atomicWithFile, atomicWriteFileWithMode, atomicWithFileAndMode) where
14 |
15 | import System.AtomicWrite.Internal (closeAndRename, maybeSetFileMode,
16 | tempFileFor)
17 |
18 | import System.IO (Handle, hPutStr, hSetBinaryMode)
19 |
20 | import System.Posix.Types (FileMode)
21 |
22 | -- | Creates or modifies a file atomically on POSIX-compliant
23 | -- systems while preserving permissions.
24 | atomicWriteFile ::
25 | FilePath -- ^ The path where the file will be updated or created
26 | -> String -- ^ The content to write to the file
27 | -> IO ()
28 | atomicWriteFile = (. flip hPutStr) . atomicWithFile
29 |
30 |
31 | -- | Creates or modifies a file atomically on
32 | -- POSIX-compliant systems and updates permissions
33 | atomicWriteFileWithMode ::
34 | FileMode -- ^ The mode to set the file to
35 | -> FilePath -- ^ The path where the file will be updated or created
36 | -> String -- ^ The content to write to the file
37 | -> IO ()
38 | atomicWriteFileWithMode mode = ( . flip hPutStr)
39 | . atomicWithFileAndMode mode
40 |
41 | -- | A general version of 'atomicWriteFile'
42 | atomicWithFile :: FilePath -> (Handle -> IO ()) -> IO ()
43 | atomicWithFile = atomicWithFileAndMaybeMode Nothing
44 |
45 | -- | A general version of 'atomicWriteFileWithMode'
46 | atomicWithFileAndMode :: FileMode
47 | -> FilePath
48 | -> (Handle -> IO ())
49 | -> IO ()
50 | atomicWithFileAndMode = atomicWithFileAndMaybeMode . Just
51 |
52 | -- | Helper function
53 | atomicWithFileAndMaybeMode :: Maybe FileMode
54 | -> FilePath
55 | -> (Handle -> IO ())
56 | -> IO ()
57 | atomicWithFileAndMaybeMode mmode path action =
58 | tempFileFor path >>= \(tmpPath, h) -> hSetBinaryMode h True
59 | >> action h
60 | >> closeAndRename h tmpPath path
61 | >> maybeSetFileMode path mmode
62 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/Text.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.Text
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a Text
11 | -- to a file.
12 |
13 | module System.AtomicWrite.Writer.Text (atomicWriteFile, atomicWriteFileWithMode) where
14 |
15 | import System.AtomicWrite.Internal (atomicWriteFileMaybeModeText)
16 |
17 | import Data.Text (Text)
18 |
19 | import Data.Text.IO (hPutStr)
20 |
21 | import System.Posix.Types (FileMode)
22 |
23 | -- | Creates a file atomically on POSIX-compliant
24 | -- systems while preserving permissions.
25 | atomicWriteFile ::
26 | FilePath -- ^ The path where the file will be updated or created
27 | -> Text -- ^ The content to write to the file
28 | -> IO ()
29 | atomicWriteFile =
30 | atomicWriteFileMaybeMode Nothing
31 |
32 | -- | Creates or modifies a file atomically on
33 | -- POSIX-compliant systems and updates permissions
34 | atomicWriteFileWithMode ::
35 | FileMode -- ^ The mode to set the file to
36 | -> FilePath -- ^ The path where the file will be updated or created
37 | -> Text -- ^ The content to write to the file
38 | -> IO ()
39 | atomicWriteFileWithMode =
40 | atomicWriteFileMaybeMode . Just
41 |
42 | -- Helper Function
43 | atomicWriteFileMaybeMode ::
44 | Maybe FileMode -- ^ The mode to set the file to
45 | -> FilePath -- ^ The path where the file will be updated or created
46 | -> Text -- ^ The content to write to the file
47 | -> IO ()
48 | atomicWriteFileMaybeMode mmode path = atomicWriteFileMaybeModeText mmode path hPutStr
49 |
--------------------------------------------------------------------------------
/src/System/AtomicWrite/Writer/Text/Binary.hs:
--------------------------------------------------------------------------------
1 | -- |
2 | -- Module : System.AtomicWrite.Writer.Text.Binary
3 | -- Copyright : © 2015-2019 Stack Builders Inc.
4 | -- License : MIT
5 | --
6 | -- Maintainer : Stack Builders
7 | -- Stability : experimental
8 | -- Portability : portable
9 | --
10 | -- Provides functionality to dump the contents of a Text
11 | -- to a file in binary mode.
12 |
13 | module System.AtomicWrite.Writer.Text.Binary (atomicWriteFile, atomicWriteFileWithMode) where
14 |
15 | import System.AtomicWrite.Internal (atomicWriteFileMaybeModeBinary)
16 |
17 | import Data.Text (Text)
18 |
19 | import Data.Text.IO (hPutStr)
20 |
21 | import System.Posix.Types (FileMode)
22 |
23 | -- | Creates a file atomically on POSIX-compliant
24 | -- systems while preserving permissions.
25 | atomicWriteFile ::
26 | FilePath -- ^ The path where the file will be updated or created
27 | -> Text -- ^ The content to write to the file
28 | -> IO ()
29 | atomicWriteFile =
30 | atomicWriteFileMaybeMode Nothing
31 |
32 | -- | Creates or modifies a file atomically on
33 | -- POSIX-compliant systems and updates permissions
34 | atomicWriteFileWithMode ::
35 | FileMode -- ^ The mode to set the file to
36 | -> FilePath -- ^ The path where the file will be updated or created
37 | -> Text -- ^ The content to write to the file
38 | -> IO ()
39 | atomicWriteFileWithMode =
40 | atomicWriteFileMaybeMode . Just
41 |
42 | -- Helper Function
43 | atomicWriteFileMaybeMode ::
44 | Maybe FileMode -- ^ The mode to set the file to
45 | -> FilePath -- ^ The path where the file will be updated or created
46 | -> Text -- ^ The content to write to the file
47 | -> IO ()
48 | atomicWriteFileMaybeMode mmode path = atomicWriteFileMaybeModeBinary mmode path hPutStr
49 |
--------------------------------------------------------------------------------
/stack.yaml:
--------------------------------------------------------------------------------
1 | resolver: lts-21.13
2 |
--------------------------------------------------------------------------------
/stack.yaml.lock:
--------------------------------------------------------------------------------
1 | # This file was autogenerated by Stack.
2 | # You should not edit this file by hand.
3 | # For more information, please see the documentation at:
4 | # https://docs.haskellstack.org/en/stable/lock_files
5 |
6 | packages: []
7 | snapshots:
8 | - completed:
9 | sha256: 8017c7970c2a8a9510c60cc70ac245d59e0c34eb932b91d37af09fe59855d854
10 | size: 640038
11 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/13.yaml
12 | original: lts-21.13
13 |
--------------------------------------------------------------------------------